fw4spl
AppConfigManager.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-2018.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #include "fwServices/AppConfigManager.hpp"
8 
9 #include "fwServices/helper/Config.hpp"
10 #include "fwServices/op/Get.hpp"
11 #include "fwServices/registry/ActiveWorkers.hpp"
12 #include "fwServices/registry/Proxy.hpp"
13 #include "fwServices/registry/ServiceConfig.hpp"
14 #include "fwServices/registry/ServiceFactory.hpp"
15 
16 #include <fwCom/Slots.hpp>
17 #include <fwCom/Slots.hxx>
18 
19 #define FW_PROFILING_DISABLED
20 #include <fwCore/Profiling.hpp>
21 
22 #include <fwData/Composite.hpp>
23 #include <fwData/Object.hpp>
24 
25 #include <fwRuntime/Bundle.hpp>
26 #include <fwRuntime/operations.hpp>
27 
28 #include <boost/foreach.hpp>
29 #include <boost/thread/futures/wait_for_all.hpp>
30 
31 namespace fwServices
32 {
33 
34 // ------------------------------------------------------------------------
35 
36 static const ::fwCom::Slots::SlotKeyType s_ADD_OBJECTS_SLOT = "addObject";
37 static const ::fwCom::Slots::SlotKeyType s_CHANGE_OBJECTS_SLOT = "changeObject";
38 static const ::fwCom::Slots::SlotKeyType s_REMOVE_OBJECTS_SLOT = "removeObjects";
39 
40 // ------------------------------------------------------------------------
41 
43  m_proxyID(0),
44  m_isUnitTest(false)
45 {
46  newSlot(s_ADD_OBJECTS_SLOT, &AppConfigManager::addObjects, this);
47  newSlot(s_REMOVE_OBJECTS_SLOT, &AppConfigManager::removeObjects, this);
48 
50  ::fwCom::HasSlots::m_slots.setWorker( defaultWorker );
51 }
52 
53 // ------------------------------------------------------------------------
54 
56 {
57  SLM_ASSERT("Manager must be stopped before destruction.", m_state == STATE_DESTROYED);
58 }
59 
60 // ------------------------------------------------------------------------
61 
62 void AppConfigManager::setConfig(const std::string& configId, const FieldAdaptorType& replaceFields)
63 {
64  m_configId = configId;
65  m_cfgElem = registry::AppConfig::getDefault()->getAdaptedTemplateConfig( configId, replaceFields, !m_isUnitTest );
66 }
67 
68 // ------------------------------------------------------------------------
69 
70 void AppConfigManager::setConfig(const std::string& configId, const ::fwData::Composite::csptr& replaceFields)
71 {
72  m_configId = configId;
73  m_cfgElem = registry::AppConfig::getDefault()->getAdaptedTemplateConfig( configId, replaceFields, !m_isUnitTest );
74 }
75 
76 // ------------------------------------------------------------------------
77 
79 {
80  FW_PROFILE("launch");
81 
82  this->startBundle();
83  this->create();
84  this->start();
85  this->update();
86 }
87 
88 // ------------------------------------------------------------------------
89 
91 {
92  this->stop();
93  this->destroy();
94 }
95 
96 // ------------------------------------------------------------------------
97 
99 {
100  SLM_ERROR_IF("Bundle is not specified, it can not be started.", m_configId.empty());
101  if (!m_configId.empty() && !m_isUnitTest)
102  {
103  std::shared_ptr< ::fwRuntime::Bundle > bundle = registry::AppConfig::getDefault()->getBundle(m_configId);
104  SLM_INFO_IF("Bundle '" + bundle->getIdentifier() + "' (used for '" + m_configId + "') is already started !",
105  bundle->isStarted());
106  if (!bundle->isStarted())
107  {
108  bundle->start();
109  }
110  }
111 }
112 
113 // ------------------------------------------------------------------------
114 
116 {
117  SLM_ASSERT("Manager already running.", m_state == STATE_DESTROYED);
118 
119  // Create the dummy object for new services that don't work on any object
120  // For now this dummy object will also contain all the "deferred" objects
121  m_tmpRootObject = ::fwData::Composite::New();
122 
123  m_addObjectConnection = ::fwServices::OSR::getRegisterSignal()->connect( this->slot(s_ADD_OBJECTS_SLOT) );
124  m_removeObjectConnection = ::fwServices::OSR::getUnregisterSignal()->connect( this->slot(s_REMOVE_OBJECTS_SLOT) );
125 
126  this->createObjects(m_cfgElem);
127  this->createConnections();
128  this->createServices(m_cfgElem);
129 
130  m_state = STATE_CREATED;
131 }
132 
133 // ------------------------------------------------------------------------
134 
136 {
137  SLM_ASSERT("Manager must be created first.", m_state == STATE_CREATED || m_state == STATE_STOPPED);
138 
139  this->processStartItems();
140  for(auto& createdObject : m_createdObjects)
141  {
142  createdObject.second.second->startConfig();
143  }
144 
145  m_state = STATE_STARTED;
146 }
147 
148 // ------------------------------------------------------------------------
149 
151 {
152  this->processUpdateItems();
153  for(auto& createdObject : m_createdObjects)
154  {
155  createdObject.second.second->updateConfig();
156  }
157 }
158 
159 // ------------------------------------------------------------------------
160 
162 {
163  SLM_ASSERT("Manager is not started, cannot stop.", m_state == STATE_STARTED);
164 
165  ::fwServices::OSR::getRegisterSignal()->disconnect( this->slot(s_ADD_OBJECTS_SLOT) );
166  ::fwServices::OSR::getUnregisterSignal()->disconnect( this->slot(s_REMOVE_OBJECTS_SLOT) );
167 
168  // Disconnect configuration connections
169  this->destroyProxies();
170 
171  for(auto& createdObject : m_createdObjects)
172  {
173  createdObject.second.second->stopConfig();
174  }
175  this->stopStartedServices();
176 
177  OSLM_INFO("Parsing OSR after stopping the config :" << std::endl
179  m_state = STATE_STOPPED;
180 }
181 
182 // ------------------------------------------------------------------------
183 
185 {
186  SLM_ASSERT("Manager is not stopped, cannot destroy.", m_state == STATE_STOPPED || m_state == STATE_CREATED);
187 
188  for(auto& createdObject : m_createdObjects)
189  {
190  createdObject.second.second->destroyConfig();
191  }
192  this->destroyCreatedServices();
193 
194  OSLM_INFO("Parsing OSR after destroying the config :" << std::endl
196 
197  m_cfgElem.reset();
198  m_createdObjects.clear();
199  m_deferredObjects.clear();
200  m_deferredServices.clear();
201  m_deferredStartSrv.clear();
202  m_deferredUpdateSrv.clear();
203  m_servicesProxies.clear();
204 
205  m_state = STATE_DESTROYED;
206 }
207 
208 // ------------------------------------------------------------------------
209 
210 void AppConfigManager::setIsUnitTest(bool isUnitTest)
211 {
212  m_isUnitTest = isUnitTest;
213 }
214 
215 // ------------------------------------------------------------------------
216 
217 fwData::Object::sptr AppConfigManager::getConfigRoot() const
218 {
219  if (m_createdObjects.empty())
220  {
221  return m_tmpRootObject;
222  }
223  return m_createdObjects.begin()->second.first;
224 }
225 
226 // ------------------------------------------------------------------------
227 
228 fwData::Object::sptr AppConfigManager::findObject(const std::string& uid, const std::string& errMsgTail) const
229 {
230  ::fwData::Object::sptr obj;
231 
232  // Look first in objects created in this appConfig
233  auto it = m_createdObjects.find(uid);
234  if(it != m_createdObjects.end())
235  {
236  obj = it->second.first;
237  }
238  else
239  {
240  // Not found, now look in the objects that were marked as "deferred"
241  auto itDeferredObj = m_deferredObjects.find(uid);
242 
243  SLM_ASSERT(this->msgHead() + "Object '" + uid + "' has not been found" + errMsgTail,
244  itDeferredObj != m_deferredObjects.end());
245  obj = itDeferredObj->second.m_object;
246  }
247  return obj;
248 }
249 
250 // ------------------------------------------------------------------------
251 
252 ::fwData::Object::sptr AppConfigManager::getNewObject(ConfigAttribute type, ConfigAttribute uid) const
253 {
254  // Building object structure
255  SPTR(::fwRuntime::Extension) ext = ::fwRuntime::findExtension(type.first);
256  if (ext)
257  {
258  const std::string className = ::fwCore::getClassname< ::fwData::Object >();
259  SLM_ASSERT("Extension and classname are different.",
260  ext->getPoint() == className);
261 
262  // Start dll to retrieve proxy and register object
263  ext->getBundle()->start();
264  }
265 
266  ::fwData::Object::sptr obj = ::fwData::factory::New(type.first);
267  SLM_ASSERT("Factory failed to build object : " + type.first, obj);
268 
269  if (uid.second)
270  {
271  SLM_ASSERT("Object already has an UID.", !obj->hasID());
272  SLM_ASSERT("UID " << uid.first << " already exists.", !::fwTools::fwID::exist(uid.first));
273  obj->setID(uid.first);
274  }
275 
276  return obj;
277 }
278 
279 // ------------------------------------------------------------------------
280 
281 ::fwData::Object::sptr AppConfigManager::getNewObject(ConfigAttribute type, const std::string& uid) const
282 {
283  return this->getNewObject(type, ConfigAttribute(uid, true));
284 }
285 
286 // ------------------------------------------------------------------------
287 
288 ::fwData::Object::sptr AppConfigManager::getObject(ConfigAttribute type, const std::string& uid) const
289 {
290  SLM_ASSERT(this->msgHead() + "Object with UID \"" + uid + "\" doesn't exist.", ::fwTools::fwID::exist(uid));
291  ::fwData::Object::sptr obj = ::fwData::Object::dynamicCast(::fwTools::fwID::getObject(uid));
292 
293  SLM_ASSERT(this->msgHead() + "The UID '" + uid + "' does not reference any object.", obj);
294 
295  if (type.second)
296  {
297  SLM_ASSERT(this->msgHead() + "Object with UID \"" + uid +
298  "\" has a different type (\"" + obj->getClassname() + "\" != \"" + type.first + "\").",
299  type.first == obj->getClassname());
300  }
301  return obj;
302 }
303 
304 // ------------------------------------------------------------------------
305 
306 ::fwServices::IService::sptr AppConfigManager::getNewService(const std::string& uid, const std::string& implType) const
307 {
308  ::fwServices::registry::ServiceFactory::sptr srvFactory = ::fwServices::registry::ServiceFactory::getDefault();
309 
310  ::fwServices::IService::sptr srv = srvFactory->create(implType);
311 
312  SLM_ASSERT("Factory could not create service of type <" + implType + ">.", srv);
313  SLM_ASSERT("Service already has an UID.", !srv->hasID());
314 
315  SLM_ASSERT(this->msgHead() + "UID " + uid + " already exists.", !::fwTools::fwID::exist(uid));
316  if (!uid.empty())
317  {
318  srv->setID(uid);
319  }
320 
321  return srv;
322 }
323 
324 // ------------------------------------------------------------------------
325 
326 void AppConfigManager::stopStartedServices()
327 {
328  std::vector< ::fwServices::IService::SharedFutureType > futures;
329 
330  BOOST_REVERSE_FOREACH(::fwServices::IService::wptr w_srv, m_startedSrv)
331  {
332  SLM_ASSERT("Service expired.", !w_srv.expired());
333 
334  const ::fwServices::IService::sptr srv = w_srv.lock();
335  OSLM_ASSERT("Service " << srv->getID() << " already stopped.", !srv->isStopped());
336  futures.emplace_back(srv->stop());
337  }
338  m_startedSrv.clear();
339  std::for_each(futures.begin(), futures.end(), std::mem_fn(&::std::shared_future<void>::wait));
340 }
341 
342 // ------------------------------------------------------------------------
343 
344 void AppConfigManager::destroyCreatedServices()
345 {
346  BOOST_REVERSE_FOREACH(::fwServices::IService::wptr w_srv, m_createdSrv)
347  {
348  SLM_ASSERT("Service expired.", !w_srv.expired());
349 
350  const ::fwServices::IService::sptr srv = w_srv.lock();
351  OSLM_ASSERT("Service " << srv->getID() << " must be stopped before destruction.", srv->isStopped());
352  ::fwServices::OSR::unregisterService(srv);
353  }
354  m_createdSrv.clear();
355 }
356 
357 // ------------------------------------------------------------------------
358 
359 void AppConfigManager::processStartItems()
360 {
361  std::vector< ::fwServices::IService::SharedFutureType > futures;
362 
363  for(const auto& elem : m_cfgElem->getElements())
364  {
365  if (elem->getName() == "start")
366  {
367  SLM_ASSERT("Missing attribute \"uid\".", elem->hasAttribute("uid"));
368  const std::string uid = elem->getAttributeValue("uid");
369  SLM_ASSERT("\"uid\" attribute is empty.", !uid.empty());
370 
371  if(!::fwTools::fwID::exist(uid))
372  {
373  if(m_deferredServices.find(uid) != m_deferredServices.end())
374  {
375  m_deferredStartSrv.push_back(uid);
376  SLM_INFO( this->msgHead() + "Start for service '" + uid + "' will be deferred since at least one "
377  "of its data is missing. With DEBUG log level, you can know which are the "
378  "missing objects.");
379  }
380  else
381  {
382  SLM_FATAL( this->msgHead() + "Start is requested for service '" + uid +
383  "', but it does not exist.");
384  }
385  }
386  else
387  {
388  const ::fwServices::IService::sptr srv = ::fwServices::get(uid);
389  SLM_FATAL_IF( this->msgHead() + "Start is requested for service '" + uid + "', though this identifier "
390  "exists, this is not a service.", !srv);
391 
392  futures.emplace_back(srv->start());
393 
394  m_startedSrv.push_back(srv);
395  }
396 
397  }
398  }
399 
400  std::for_each(futures.begin(), futures.end(), std::mem_fn(&::std::shared_future<void>::wait));
401 }
402 
403 // ------------------------------------------------------------------------
404 
405 void AppConfigManager::processUpdateItems()
406 {
407  std::vector< ::fwServices::IService::SharedFutureType > futures;
408 
409  for(const auto& elem : m_cfgElem->getElements())
410  {
411  if (elem->getName() == "update")
412  {
413  const std::string uid = elem->getAttributeValue("uid");
414  SLM_ASSERT("\"uid\" attribute is empty.", !uid.empty());
415 
416  if(!::fwTools::fwID::exist(uid))
417  {
418  if(m_deferredServices.find(uid) != m_deferredServices.end())
419  {
420  m_deferredUpdateSrv.push_back(uid);
421  SLM_INFO( this->msgHead() + "Update for service '" + uid + "'will be deferred since at least one "
422  "of its data is missing. With DEBUG log level, you can know which are the "
423  "missing objects.");
424  }
425  else
426  {
427  SLM_FATAL( this->msgHead() + "Update is requested for service '" + uid +
428  "', but it does not exist.");
429  }
430  }
431  else
432  {
433  const ::fwServices::IService::sptr srv = ::fwServices::get(uid);
434  SLM_FATAL_IF( this->msgHead() + "Update is requested for service '" + uid +
435  "', though this identifier exists, this is not a service.", !srv);
436 
437  futures.emplace_back(srv->update());
438  }
439  }
440  }
441 
442  std::for_each(futures.begin(), futures.end(), std::mem_fn(&::std::shared_future<void>::wait));
443 }
444 
445 // ------------------------------------------------------------------------
446 
447 void AppConfigManager::createObjects(::fwRuntime::ConfigurationElement::csptr cfgElem)
448 {
449  for(const auto& elem : cfgElem->getElements())
450  {
451  if (elem->getName() == "object")
452  {
453  // Get attributes
454 
455  // Id
456  ConfigAttribute id("", false);
457  if (elem->hasAttribute("uid"))
458  {
459  id.first = elem->getAttributeValue("uid");
460  SLM_ASSERT(this->msgHead() + "\"uid\" attribute is empty.", !id.first.empty());
461  id.second = true;
462  }
463 
464  // Type
465  ConfigAttribute type("", false);
466  if (elem->hasAttribute("type"))
467  {
468  type.first = elem->getAttributeValue("type");
469  SLM_ASSERT(this->msgHead() + "\"type\" attribute is empty.", !type.first.empty());
470  SLM_ASSERT(this->msgHead() + "\"type\" must be a rooted namespace.", type.first.substr(0, 2) == "::");
471  type.second = true;
472  }
473 
474  // Build mode
475  ConfigAttribute buildMode("", false);
476  if (elem->hasAttribute("src"))
477  {
478  buildMode.first = elem->getAttributeValue("src");
479  SLM_ASSERT("this->msgHead() + \"src\" attribute is empty.", !buildMode.first.empty());
480 
481  SLM_ASSERT("Unhandled build mode (bad \"src\" attribute). Must be \"new\", \"deferred\" or \"ref\".",
482  buildMode.first == "ref" || buildMode.first == "src" || buildMode.first == "deferred");
483  buildMode.second = true;
484  }
485 
486  if(buildMode.first == "deferred")
487  {
488  SLM_ASSERT(this->msgHead() + "Missing attribute \"id\".", id.second);
489  auto ret = m_deferredObjects.insert( std::make_pair(id.first, DeferredObjectType()));
490  SLM_ASSERT(this->msgHead() + "Object '" + id.first + "' already exists in this config.", ret.second);
491  }
492  else
493  {
494  // Creation of a new object
495  ::fwData::Object::sptr obj;
496 
497  // Create new or get the referenced object
498  if (buildMode.second && buildMode.first == "ref")
499  {
500  SLM_ASSERT(this->msgHead() + "Missing attribute \"id\".", id.second);
501  obj = this->getObject(type, id.first);
502  }
503  else
504  {
505  obj = this->getNewObject(type, id);
506  }
507 
508  // Get the object parser associated with the object type
509  const auto srvFactory = ::fwServices::registry::ServiceFactory::getDefault();
510 
511  std::string srvImpl = srvFactory->getDefaultImplementationIdFromObjectAndType(
512  obj->getClassname(), "::fwServices::IXMLParser");
513 
514  ::fwServices::IService::sptr srv = srvFactory->create("::fwServices::IXMLParser", srvImpl);
515  auto objectParser = ::fwServices::IXMLParser::dynamicCast(srv);
516  objectParser->setObjectConfig(elem);
517  objectParser->createConfig(obj);
518 
519  m_createdObjects[id.first] = std::make_pair( obj, objectParser);
520  }
521  }
522  }
523 }
524 
525 // ------------------------------------------------------------------------
526 
527 void AppConfigManager::createServices(::fwRuntime::ConfigurationElement::csptr cfgElem)
528 {
529  for(const auto& elem : cfgElem->getElements())
530  {
531  if (elem->getName() == "service")
532  {
533  // Parse the service configuration
534  ServiceConfig srvConfig = ::fwServices::helper::Config::parseService(elem, this->msgHead());
535 
536  // Check if we can start the service now or if we must deferred its creation
537  bool createService = true;
538  std::vector<std::string> uids;
539 
540  for(const auto& objectCfg : srvConfig.m_objects)
541  {
542  // If the current service uses an object that is marked as deferred, this means
543  // we will have to manage automatically the start/stop and the connections
544  auto it = m_deferredObjects.find(objectCfg.m_uid);
545  if(it != m_deferredObjects.end())
546  {
547  it->second.m_servicesCfg.emplace_back(srvConfig);
548  uids.push_back(objectCfg.m_uid);
549  m_deferredServices.insert(srvConfig.m_uid);
550 
551  if(!objectCfg.m_optional)
552  {
553  createService = false;
554  }
555  }
556  else
557  {
558  SLM_ERROR_IF(
559  this->msgHead() + "Object '" + objectCfg.m_uid + "' is not deferred but it is used "
560  "as an optional key in service '" + srvConfig.m_uid + "'. This is useless, so maybe you "
561  "intended to use a deferred object instead ?", objectCfg.m_optional);
562  }
563 
564  // Extra check to warn the user that an object is used as output but not marked as deferred
565  if(objectCfg.m_access == ::fwServices::IService::AccessType::OUTPUT)
566  {
567  SLM_ERROR_IF(this->msgHead() + "Object '" + objectCfg.m_uid + "' is used as output in service '" +
568  srvConfig.m_uid + "' but it not declared as 'deferred'.",
569  it == m_deferredObjects.end());
570  }
571  }
572 
573  if(createService)
574  {
575  this->createService(srvConfig);
576  }
577  else
578  {
579  // Check if a service hasn't been already created with this uid
580  SLM_ASSERT(this->msgHead() + "UID " + srvConfig.m_uid + " already exists.",
581  !::fwTools::fwID::exist(srvConfig.m_uid));
582 
583  const std::string msg = AppConfigManager::getUIDListAsString(uids);
584  SLM_INFO(this->msgHead() + "Service '" + srvConfig.m_uid +
585  "' has not been created because the object" + msg + "not available.");
586  }
587  }
588  else if (elem->getName() == "serviceList")
589  {
590  this->createServices(elem);
591  }
592  }
593 }
594 
595 // ------------------------------------------------------------------------
596 
597 ::fwServices::IService::sptr AppConfigManager::createService(const ::fwServices::IService::Config& srvConfig)
598 {
599  // Create and bind service
600  const ::fwServices::IService::sptr srv = this->getNewService(srvConfig.m_uid, srvConfig.m_type);
601  m_createdSrv.push_back(srv);
602 
603  if (!srvConfig.m_worker.empty())
604  {
605  ::fwServices::registry::ActiveWorkers::sptr activeWorkers = ::fwServices::registry::ActiveWorkers::getDefault();
606  ::fwThread::Worker::sptr worker;
607  worker = activeWorkers->getWorker(srvConfig.m_worker);
608  if (!worker)
609  {
610  worker = ::fwThread::Worker::New();
611  activeWorkers->addWorker(srvConfig.m_worker, worker);
612  }
613  srv->setWorker(worker);
614  }
615 
616  std::string errMsgTail = " when creating service '" + srvConfig.m_uid + "'.";
617 
618  for(const auto& objectCfg : srvConfig.m_objects)
619  {
620  srv->setObjectId(objectCfg.m_key, objectCfg.m_uid);
621 
622  ::fwData::Object::sptr obj = this->findObject(objectCfg.m_uid, errMsgTail);
623 
624  SLM_ASSERT(this->msgHead() + "Object '" + objectCfg.m_uid + "' has not been found" + errMsgTail,
625  (!objectCfg.m_optional && obj) || objectCfg.m_optional);
626  if((obj || !objectCfg.m_optional) && objectCfg.m_access != ::fwServices::IService::AccessType::OUTPUT)
627  {
628  ::fwServices::OSR::registerService(obj, objectCfg.m_key, objectCfg.m_access, srv);
629  }
630  }
631 
632  // Ok for now we assume we always need the root composite
633  // This is only true for services that will need to perform some stuff like adding new objects, etc...
634  // We will see in the future if this should be replaced or not
635  {
636  srv->setObjectId(::fwServices::IService::s_DEFAULT_OBJECT, "defaultObjectId");
637 
638  ::fwServices::OSR::registerService(m_tmpRootObject, ::fwServices::IService::s_DEFAULT_OBJECT,
639  ::fwServices::IService::AccessType::INOUT, srv);
640  }
641 
642  // Set the size of the key groups
643  srv->m_keyGroupSize = srvConfig.m_groupSize;
644 
645  // Set the proxies
646  const auto& itSrvProxy = m_servicesProxies.find(srvConfig.m_uid);
647  if(itSrvProxy != m_servicesProxies.end())
648  {
649  for(const auto& itProxy : itSrvProxy->second.m_proxyCnt)
650  {
651  srv->addProxyConnection(itProxy.second);
652  }
653  }
654 
655  // Configure
656  srv->setConfiguration(srvConfig);
657  srv->configure();
658 
659  return srv;
660 }
661 
662 // ------------------------------------------------------------------------
663 
664 void AppConfigManager::createConnections()
665 {
666  for(const auto& elem : m_cfgElem->getElements())
667  {
668  if (elem->getName() == "connect")
669  {
670  // Parse all connections
671  auto genIdFn = [this]()
672  {
673  return "Proxy_" + this->getID() + "_" + std::to_string(m_proxyID++);
674  };
675 
676  ProxyConnections connectionInfos = ::fwServices::helper::Config::parseConnections2(elem, this->msgHead(),
677  genIdFn);
678  // Proxy that is used for non-deferred connections
679  ProxyConnections createdObjectsProxy(connectionInfos.m_channel);
680 
681  // Register signals
682  for(const auto& signalInfo : connectionInfos.m_signals)
683  {
684  auto itDeferredObj = m_deferredObjects.find(signalInfo.first);
685  if( itDeferredObj != m_deferredObjects.end() )
686  {
687  // Deferred Object
688  ProxyConnections& proxy = itDeferredObj->second.m_proxyCnt[connectionInfos.m_channel];
689  proxy.addSignalConnection(signalInfo);
690  }
691  else
692  {
693  auto itObj = m_createdObjects.find(signalInfo.first);
694  if( itObj != m_createdObjects.end() )
695  {
696  // Regular object
697  createdObjectsProxy.addSignalConnection(signalInfo);
698  }
699  else
700  {
701  // Service
702  auto& itSrv = m_servicesProxies[signalInfo.first];
703  ProxyConnections& proxy = itSrv.m_proxyCnt[connectionInfos.m_channel];
704  proxy.addSignalConnection(signalInfo);
705  proxy.m_channel = connectionInfos.m_channel;
706  }
707  }
708  }
709 
710  // Register slots
711  for(const auto& slotInfo : connectionInfos.m_slots)
712  {
713  auto itDeferredObj = m_deferredObjects.find(slotInfo.first);
714  if( itDeferredObj != m_deferredObjects.end() )
715  {
716  // Deferred Object
717  ProxyConnections& proxy = itDeferredObj->second.m_proxyCnt[connectionInfos.m_channel];
718  proxy.addSlotConnection(slotInfo);
719  }
720  else
721  {
722  auto itObj = m_createdObjects.find(slotInfo.first);
723  if( itObj != m_createdObjects.end() )
724  {
725  // Regular object
726  createdObjectsProxy.addSlotConnection(slotInfo);
727  }
728  else
729  {
730  // Service
731  auto& itSrv = m_servicesProxies[slotInfo.first];
732  ProxyConnections& proxy = itSrv.m_proxyCnt[connectionInfos.m_channel];
733  proxy.addSlotConnection(slotInfo);
734  proxy.m_channel = connectionInfos.m_channel;
735  }
736  }
737  }
738 
739  m_createdObjectsProxies[connectionInfos.m_channel] = createdObjectsProxy;
740  this->connectProxy(connectionInfos.m_channel, createdObjectsProxy);
741  }
742  }
743 }
744 
745 // ------------------------------------------------------------------------
746 
747 void AppConfigManager::destroyProxy(const std::string& channel, const ProxyConnections& proxyCfg,
748  const std::string& key, ::fwData::Object::csptr hintObj)
749 {
750  ::fwServices::registry::Proxy::sptr proxy = ::fwServices::registry::Proxy::getDefault();
751 
752  for(const ProxyConnections::ProxyEltType& signalElt : proxyCfg.m_signals)
753  {
754  if(key.empty() || signalElt.first == key)
755  {
756  ::fwTools::Object::csptr obj = hintObj;
757  if(obj == nullptr)
758  {
759  obj = ::fwTools::fwID::getObject(signalElt.first);
760  }
761  ::fwCom::HasSignals::csptr hasSignals = std::dynamic_pointer_cast< const ::fwCom::HasSignals >(obj);
762  SLM_ASSERT(this->msgHead() + "Signal source not found '" + signalElt.first + "'", obj);
763 
764  ::fwCom::SignalBase::sptr sig = hasSignals->signal(signalElt.second);
765 
766  try
767  {
768  proxy->disconnect(channel, sig);
769  }
770  catch (const std::exception& e)
771  {
772  SLM_ERROR("Signal '" + signalElt.second + "' from '" + signalElt.first + "' can not be disconnected "
773  "from the channel '" + channel + "': " + std::string(e.what()));
774  }
775  }
776  }
777  for(const ProxyConnections::ProxyEltType& slotElt : proxyCfg.m_slots)
778  {
779  if(key.empty() || slotElt.first == key)
780  {
781  ::fwTools::Object::sptr obj = ::fwTools::fwID::getObject(slotElt.first);
782  ::fwCom::HasSlots::sptr hasSlots = std::dynamic_pointer_cast< ::fwCom::HasSlots >(obj);
783  SLM_ASSERT(this->msgHead() + "Slot destination not found '" + slotElt.first + "'", hasSlots);
784 
785  ::fwCom::SlotBase::sptr slot = hasSlots->slot(slotElt.second);
786 
787  try
788  {
789  proxy->disconnect(channel, slot);
790  }
791  catch (const std::exception& e)
792  {
793  SLM_ERROR("Slot '" + slotElt.second + "' from '" + slotElt.first + "' can not be disconnected from the "
794  "channel '" + channel + "': " + std::string(e.what()));
795  }
796  }
797  }
798 }
799 
800 // ------------------------------------------------------------------------
801 
802 void AppConfigManager::destroyProxies()
803 {
804  // Remove local proxies from deferred objects
805  for(auto& itDeferredObj : m_deferredObjects)
806  {
807  if(itDeferredObj.second.m_object)
808  {
809  for(const auto& itProxy : itDeferredObj.second.m_proxyCnt)
810  {
811  this->destroyProxy(itProxy.first, itProxy.second, itDeferredObj.first, itDeferredObj.second.m_object);
812  }
813  itDeferredObj.second.m_object.reset();
814  }
815  }
816 
817  // Remove local proxies from all created objects
818  for(const auto& itProxy : m_createdObjectsProxies)
819  {
820  this->destroyProxy(itProxy.first, itProxy.second);
821  }
822 
823  m_createdObjectsProxies.clear();
824 }
825 
826 //------------------------------------------------------------------------------
827 
828 void AppConfigManager::addObjects(fwData::Object::sptr obj, const std::string& id)
829 {
830  FW_PROFILE("addObjects");
831 
832  // Local map used to process services only once
833  std::map< std::string, const ServiceConfig* > servicesMapCfg;
834 
835  // Local vector used to store services and keep the declare order (we could use only this one but the map is used
836  // to speedup the search
837  std::vector< const ServiceConfig* > servicesCfg;
838 
839  // Are there services that were waiting for this object ?
840  auto itDeferredObj = m_deferredObjects.find(id);
841  if(itDeferredObj != m_deferredObjects.end())
842  {
843  // For each service waiting to be started
844  for(const auto& srvCfg : itDeferredObj->second.m_servicesCfg)
845  {
846  if(servicesMapCfg.find(srvCfg.m_uid) == servicesMapCfg.end())
847  {
848  servicesMapCfg[srvCfg.m_uid] = &srvCfg;
849  servicesCfg.push_back(&srvCfg);
850  }
851  }
852 
853  // Connect signals of this deferred object
854  itDeferredObj->second.m_object = obj;
855 
856  for(const auto& connectCfg : itDeferredObj->second.m_proxyCnt)
857  {
858  this->connectProxy(connectCfg.first, connectCfg.second);
859  }
860  }
861 
862  std::unordered_map<std::string, ::fwServices::IService::sptr> newServices;
863 
864  for(const auto& itService : servicesCfg)
865  {
866  auto srvCfg = itService;
867  SLM_ASSERT("Config is null", srvCfg);
868  auto& uid = srvCfg->m_uid;
869 
870  bool createService = true;
871 
872  // Look for all objects (there could be more than the current object) and check if they are all created
873  for(const auto& objCfg : srvCfg->m_objects)
874  {
875  // Look first in objects created in this appConfig
876  if(m_createdObjects.find(objCfg.m_uid) == m_createdObjects.end())
877  {
878  // Not found, now look in the objects that were marked as "deferred"
879  const auto itDeferredObj = m_deferredObjects.find(objCfg.m_uid);
880  SLM_ASSERT( this->msgHead() + "Object '" + objCfg.m_uid + "' used by service '" + uid +
881  "' has not been declared in this AppConfig.",
882  itDeferredObj != m_deferredObjects.end());
883 
884  const auto object = itDeferredObj->second.m_object;
885  if(object == nullptr)
886  {
887  if(!objCfg.m_optional)
888  {
889  createService = false;
890 
891  SLM_INFO( this->msgHead() + "Service '" + uid + "' has not been created because the "
892  "object" + objCfg.m_uid + " is not available.");
893  }
894  }
895  else if(::fwTools::fwID::exist(uid))
896  {
897  ::fwServices::IService::sptr srv = ::fwServices::get(uid);
898  SLM_ASSERT(this->msgHead() + "No service registered with UID \"" + uid + "\".", srv);
899 
900  // We have an optional object
901  if( objCfg.m_optional)
902  {
903  // Check if we already registered an object at this key
904  auto registeredObj = ::fwServices::OSR::getRegistered(objCfg.m_key, objCfg.m_access, srv);
905  if(registeredObj != nullptr)
906  {
907  // If this is not the object we have to swap, then unregister it
908  if(registeredObj != object)
909  {
910  ::fwServices::OSR::unregisterService(objCfg.m_key, objCfg.m_access, srv);
911  }
912  }
913 
914  if(registeredObj != object)
915  {
916  // Register the key on the service
917  ::fwServices::OSR::registerService(object, objCfg.m_key, objCfg.m_access, srv);
918 
919  // Call the swapping callback of the service and wait for it
920  srv->swapKey(objCfg.m_key, ::fwData::Object::constCast(registeredObj)).wait();
921  }
922  }
923 
924  createService = false;
925  }
926  }
927  }
928 
929  // All the data for this service is ready, create and run it
930  if(createService)
931  {
932  // Create the service
933  newServices.emplace(std::make_pair(uid, this->createService(*srvCfg)));
934 
935  // Debug message
936  SLM_INFO( this->msgHead() + "Service '" + uid + "' has been automatically created because its "
937  "objects are all available.");
938  }
939  }
940 
941  std::vector< ::fwServices::IService::SharedFutureType > futures;
942 
943  // Start services according to the order given in the configuration
944  for(const auto& uid : m_deferredStartSrv)
945  {
946  auto itSrv = newServices.find(uid);
947  if( itSrv != newServices.end())
948  {
949  futures.push_back(itSrv->second->start());
950  m_startedSrv.push_back(itSrv->second);
951 
952  // Debug message
953  SLM_INFO( this->msgHead() + "Service '" + uid + "' has been automatically started because its "
954  "objects are all available.");
955  }
956  }
957 
958  ::boost::wait_for_all(futures.begin(), futures.end());
959  futures.clear();
960 
961  // Update services according to the order given in the configuration
962  for(const auto& uid : m_deferredUpdateSrv)
963  {
964  auto itSrv = newServices.find(uid);
965  if( itSrv != newServices.end())
966  {
967  futures.push_back(itSrv->second->update());
968 
969  // Debug message
970  SLM_INFO( this->msgHead() + "Service '" + uid + "' has been automatically update because its "
971  "objects are all available.");
972  }
973  }
974 
975  ::boost::wait_for_all(futures.begin(), futures.end());
976 }
977 
978 //------------------------------------------------------------------------------
979 
980 void AppConfigManager::removeObjects(fwData::Object::sptr obj, const std::string& id)
981 {
982  FW_PROFILE("removeObjects");
983 
984  // Are there services that were connected with this object ?
985  const auto itDeferredObj = m_deferredObjects.find(id);
986  if(itDeferredObj != m_deferredObjects.end())
987  {
988  for(const auto& itProxy : itDeferredObj->second.m_proxyCnt)
989  {
990  this->destroyProxy(itProxy.first, itProxy.second, id, itDeferredObj->second.m_object);
991  }
992 
993  itDeferredObj->second.m_object.reset();
994 
995  // Are there services that were using this object ?
996  for(const auto& srvCfg : itDeferredObj->second.m_servicesCfg)
997  {
998  if( ::fwTools::fwID::exist(srvCfg.m_uid) )
999  {
1000  // Check all objects, to know if this object is optional
1001  bool optional = true;
1002 
1003  for(const auto& objCfg : srvCfg.m_objects)
1004  {
1005  if(id == objCfg.m_uid)
1006  {
1007  ::fwServices::IService::sptr srv = ::fwServices::get(srvCfg.m_uid);
1008  OSLM_ASSERT("No service registered with UID \"" << srvCfg.m_uid << "\".", srv);
1009 
1010  if(objCfg.m_optional)
1011  {
1012  if(::fwServices::OSR::isRegistered(objCfg.m_key, objCfg.m_access, srv))
1013  {
1014  ::fwServices::OSR::unregisterService(objCfg.m_key, objCfg.m_access, srv);
1015 
1016  srv->swapKey(objCfg.m_key, obj).wait();
1017  }
1018  }
1019  else
1020  {
1021  optional = false;
1022  }
1023  }
1024  }
1025 
1026  if(!optional)
1027  {
1028  // 1. Stop the service
1029  ::fwServices::IService::sptr srv = ::fwServices::get(srvCfg.m_uid);
1030  OSLM_ASSERT(this->msgHead() + "No service registered with UID \"" << srvCfg.m_uid << "\".", srv);
1031 
1032  OSLM_ASSERT("Service " << srv->getID() << " already stopped.", !srv->isStopped());
1033  srv->stop().wait();
1034 
1035  for(auto it = m_startedSrv.begin(); it != m_startedSrv.end(); ++it)
1036  {
1037  if(it->lock() == srv)
1038  {
1039  m_startedSrv.erase(it);
1040  break;
1041  }
1042  }
1043 
1044  // 2. Destroy the service
1045  OSLM_ASSERT("Service " << srv->getID() << " must be stopped before destruction.",
1046  srv->isStopped());
1047  ::fwServices::OSR::unregisterService(srv);
1048 
1049  for(auto it = m_createdSrv.begin(); it != m_createdSrv.end(); ++it)
1050  {
1051  if(it->lock() == srv)
1052  {
1053  m_createdSrv.erase(it);
1054  break;
1055  }
1056  }
1057 
1058  ::fwServices::IService::wptr checkSrv = srv;
1059  srv.reset();
1060 
1061  SLM_ASSERT( this->msgHead() + "The service '" + srvCfg.m_uid +
1062  "'' should have been destroyed, but someone "
1063  "still holds a reference which prevents to destroy it properly.",
1064  checkSrv.expired());
1065 
1066  SLM_INFO( this->msgHead() + "Service '" + srvCfg.m_uid +
1067  "' has been stopped because the object " +
1068  id + " is no longer available.");
1069  }
1070  else
1071  {
1072  // Update auto connections
1073  ::fwServices::IService::sptr srv = ::fwServices::get(srvCfg.m_uid);
1074  OSLM_ASSERT(this->msgHead() + "No service registered with UID \"" << srvCfg.m_uid << "\".", srv);
1075 
1076  srv->autoDisconnect();
1077  srv->autoConnect();
1078  }
1079  }
1080  }
1081  }
1082 }
1083 
1084 //------------------------------------------------------------------------------
1085 
1086 void AppConfigManager::connectProxy(const std::string& channel, const ProxyConnections& connectCfg)
1087 {
1088  ::fwServices::registry::Proxy::sptr proxy = ::fwServices::registry::Proxy::getDefault();
1089 
1090  for(const auto& signalCfg : connectCfg.m_signals)
1091  {
1092  ::fwTools::Object::sptr sigSource = ::fwTools::fwID::getObject(signalCfg.first);
1093  if(sigSource == nullptr)
1094  {
1095  // We didn't found the object or service globally, let's try with local deferred objects
1096  auto itDeferredObj = m_deferredObjects.find(signalCfg.first);
1097  SLM_ASSERT( this->msgHead() + "Object '" + signalCfg.first + "' not found.",
1098  itDeferredObj != m_deferredObjects.end());
1099 
1100  sigSource = itDeferredObj->second.m_object;
1101  }
1102  ::fwCom::HasSignals::sptr hasSignals = std::dynamic_pointer_cast< ::fwCom::HasSignals >(sigSource);
1103  SLM_ASSERT(this->msgHead() + "Signal source not found '" + signalCfg.first + "'", hasSignals);
1104 
1105  ::fwCom::SignalBase::sptr sig = hasSignals->signal(signalCfg.second);
1106  SLM_ASSERT("Signal '" + signalCfg.second + "' not found in source '" + signalCfg.first + "'.", sig);
1107 
1108  try
1109  {
1110  proxy->connect(channel, sig);
1111  }
1112  catch (const std::exception& e)
1113  {
1114  SLM_ERROR("Signal '" + signalCfg.second + "' from '" + signalCfg.first + "' can not be connected to the "
1115  "channel '" + channel + "': " + std::string(e.what()));
1116  }
1117  }
1118 
1119  for(const auto& slotCfg : connectCfg.m_slots)
1120  {
1121  ::fwTools::Object::sptr slotDest = ::fwTools::fwID::getObject(slotCfg.first);
1122  ::fwCom::HasSlots::sptr hasSlots = std::dynamic_pointer_cast< ::fwCom::HasSlots >(slotDest);
1123  SLM_ASSERT(this->msgHead() + "Slot destination not found '" + slotCfg.first + "'", hasSlots);
1124 
1125  ::fwCom::SlotBase::sptr slot = hasSlots->slot(slotCfg.second);
1126  SLM_ASSERT("Slot '" + slotCfg.second + "' not found in source '" + slotCfg.first + "'.", slot);
1127 
1128  try
1129  {
1130  proxy->connect(channel, slot);
1131  }
1132  catch (const std::exception& e)
1133  {
1134  SLM_ERROR("Slot '" + slotCfg.second + "' from '" + slotCfg.first + "' can not be connected to the "
1135  "channel '" + channel + "': " + std::string(e.what()));
1136  }
1137  }
1138 }
1139 
1140 //------------------------------------------------------------------------------
1141 
1142 std::string AppConfigManager::getUIDListAsString(const std::vector<std::string>& uidList)
1143 {
1144  std::string msg = uidList.size() == 1 ? " '" : "s '";
1145  msg += uidList[0];
1146  for(auto it = uidList.begin() + 1; it < uidList.end(); ++it)
1147  {
1148  msg += "', '" + *it;
1149  }
1150  msg = uidList.size() == 1 ? msg + "' is " : msg + "' are ";
1151 
1152  return msg;
1153 }
1154 
1155 //------------------------------------------------------------------------------
1156 
1157 } // namespace fwServices
#define SPTR(_cls_)
virtual FWSERVICES_API void launch() override
Calls methods : create, start then update.
static FWSERVICES_API const std::string s_DEFAULT_OBJECT
Definition: IService.hpp:113
FWSERVICES_API AppConfigManager()
Constructor. Do nothing.
#define OSLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:310
static FWSERVICES_API::fwServices::IService::Config parseService(const std::shared_ptr< const ::fwRuntime::ConfigurationElement > &srvElem, const std::string &errMsgHead)
Parse a service and return a service configuration.
Definition: Config.cpp:262
Defines the extension class.
Definition: Extension.hpp:30
static FWSERVICES_API Proxy::sptr getDefault()
Returns an instance of Proxy.
Definition: Proxy.cpp:33
::fwRuntime::ConfigurationElement::csptr m_cfgElem
XML Configuration tree.
static FWTOOLS_API bool exist(IDType _id)
Definition: fwID.cpp:33
virtual FWSERVICES_API void destroy() override
Destroys services specified in config.
Namespace fwServices is dedicated to (mimic) the dynamic affectation of methods to (pure data) object...
This class proposes a mapping between a SlotKeyType and a SlotBase.
Definition: HasSlots.hpp:22
virtual FWSERVICES_API void stopAndDestroy() override
Stops and destroys services specified in config, then resets the configRoot sptr. ...
#define OSLM_INFO(message)
Definition: spyLog.hpp:252
virtual FWSERVICES_API void update() override
Updates services specified in config.
FWSERVICES_API std::string getRegistryInformation()
Wraps ObjectService::getRegistryInformation.
virtual FWSERVICES_API void setConfig(const std::string &configId, const FieldAdaptorType &replaceFields=FieldAdaptorType()) override
Set configuration.
virtual FWSERVICES_API::fwData::Object::sptr getConfigRoot() const override
Get config root.
#define SLM_ERROR(message)
Definition: spyLog.hpp:272
virtual FWSERVICES_API void create() override
Creates objects and services from config.
FWTOOLS_API IDType getID(Policy policy=GENERATE) const
Returns the id of the object. If it is not set and the policy value is.
Definition: fwID.cpp:78
#define SLM_FATAL(message)
Definition: spyLog.hpp:283
virtual FWSERVICES_API void start() override
Starts services specified in config.
#define SLM_INFO_IF(message, cond)
Definition: spyLog.hpp:254
#define SLM_ERROR_IF(message, cond)
Definition: spyLog.hpp:276
static FWSERVICES_API ActiveWorkers::sptr getDefault()
Returns an instance of ActiveWorkers.
#define SLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:308
Helper class to register proxy connections.
virtual FWSERVICES_API void startBundle()
Starts the bundle associated to the config.
#define SLM_FATAL_IF(message, cond)
Definition: spyLog.hpp:287
static FWSERVICES_API::fwThread::Worker::sptr getDefaultWorker()
Get the default registered worker.
virtual FWSERVICES_API ~AppConfigManager()
Destructor. Do nothing.
FWSERVICES_API void setIsUnitTest(bool isUnitTest)
Set it to true if we are testing the class.
static FWSERVICES_API AppConfig::sptr getDefault()
Return an instance of AppConfig.
Definition: AppConfig.cpp:37
static FWSERVICES_API ServiceFactory::sptr getDefault()
Return the unique Instance, create it if required at first access.
#define SLM_INFO(message)
Definition: spyLog.hpp:250
FWSERVICES_API bool isRegistered(const ::fwServices::IService::KeyType &objKey,::fwServices::IService::AccessType access,::fwServices::IService::sptr service)
Return true if a key is registered for a given service.
static FWSERVICES_API ProxyConnections parseConnections2(const std::shared_ptr< const ::fwRuntime::ConfigurationElement > &connectionCfg, const std::string &errMsgHead, std::function< std::string()> generateChannelNameFn)
Parses "<connect>" tags from given configuration and return a structure containing the signal and slo...
Definition: Config.cpp:113
Used to store a service configuration.
Definition: IService.hpp:100
This class proposes a mapping between a SignalKeyType and a SignalBase.
Definition: HasSignals.hpp:21
ConfigState m_state
Running state of the app config manager.
static FWTOOLS_API std::shared_ptr< ::fwTools::Object > getObject(IDType requestID)
Retrieve the object attached to the given id. Return a null sptr if no correspondence exist...
Definition: fwID.cpp:117
virtual FWSERVICES_API void stop() override
Stops services specified in config.