Apache Celix  2.3.0
An implementation of the OSGi specification adapted to C and C++
Component_Impl.h
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 using namespace celix::dm;
21 
22 inline void BaseComponent::runBuild() {
23  if (context == nullptr || cDepMan == nullptr) {
24  return;
25  }
26 
27  {
28  std::lock_guard<std::mutex> lck{mutex};
29  for (auto& provide : providedServices) {
30  provide->runBuild();
31  }
32  }
33 
34  {
35  std::lock_guard<std::mutex> lck{mutex};
36  for (auto &dep : dependencies) {
37  dep->runBuild();
38  }
39  }
40 
41 
42  bool alreadyAdded = cmpAddedToDepMan.exchange(true);
43  if (!alreadyAdded) {
44  celix_dependencyManager_addAsync(cDepMan, cCmp);
45  }
46 }
47 
48 inline BaseComponent::~BaseComponent() noexcept = default;
49 
50 inline void BaseComponent::wait() const {
51  celix_dependencyManager_wait(cDepMan);
52 }
53 
54 std::ostream& celix::dm::operator<<(std::ostream &out, const BaseComponent &cmp) {
55  char* buf = nullptr;
56  size_t bufSize = 0;
57  FILE* stream = open_memstream(&buf, &bufSize);
58  celix_dm_component_info_t* cmpInfo = nullptr;
60  celix_dmComponent_printComponentInfo(cmpInfo, true, true, stream);
61  fclose(stream);
63  out << buf;
64  free(buf);
65  return out;
66 }
67 
68 template<class T>
70  celix_bundle_context_t *context,
71  celix_dependency_manager_t* cDepMan,
72  std::string name,
73  std::string uuid) : BaseComponent(
74  context,
75  cDepMan,
76  celix::cmpTypeName<T>(name),
77  std::move(uuid)) {}
78 
79 template<class T>
80 Component<T>::~Component() = default;
81 
82 template<class T>
83 template<class I>
84 Component<T>& Component<T>::addInterfaceWithName(const std::string &serviceName, const std::string &version, const Properties &properties) {
85  static_assert(std::is_base_of<I,T>::value, "Component T must implement Interface I");
86  if (!serviceName.empty()) {
87  T* cmpPtr = &this->getInstance();
88  I* svcPtr = static_cast<I*>(cmpPtr); //NOTE T should implement I
89  std::shared_ptr<I> svc{svcPtr, [](I*){/*nop*/}};
90  auto provide = std::make_shared<ProvidedService<T,I>>(cComponent(), serviceName, std::move(svc), true);
91  provide->setVersion(version);
92  provide->setProperties(properties);
93  std::lock_guard<std::mutex> lck{mutex};
94  providedServices.push_back(provide);
95  } else {
96  std::cerr << "Cannot add interface with a empty name\n";
97  }
98 
99  return *this;
100 }
101 
102 template<class T>
103 template<class I>
104 Component<T>& Component<T>::addInterface(const std::string &version, const Properties &properties) {
105  static_assert(std::is_base_of<I,T>::value, "Component T must implement Interface I");
106  std::string serviceName = typeName<I>();
107  if (serviceName.empty()) {
108  std::cerr << "Cannot add interface, because type name could not be inferred. function: '" << __PRETTY_FUNCTION__ << "'\n";
109  }
110  auto svcVersion = celix::typeVersion<I>(version);
111  if (!svcVersion.empty()) {
112  properties[celix::SERVICE_VERSION] = std::move(svcVersion);
113  }
114 
115  return this->addInterfaceWithName<I>(serviceName, version, properties);
116 }
117 
118 template<class T>
119 template<class I>
120 Component<T>& Component<T>::addCInterface(I* svcPtr, const std::string &serviceName, const std::string &version, const Properties &properties) {
121  std::shared_ptr<I> svc{svcPtr, [](I*){/*nop*/}};
122  auto provide = std::make_shared<ProvidedService<T,I>>(cComponent(), serviceName, std::move(svc), false);
123  provide->setVersion(version);
124  provide->setProperties(properties);
125  std::lock_guard<std::mutex> lck{mutex};
126  providedServices.push_back(provide);
127  return *this;
128 }
129 
130 template<class T>
131 template<class I>
133  celix_dmComponent_removeInterface(this->cComponent(), svc);
134  std::lock_guard<std::mutex> lck{mutex};
135  for (auto it = providedServices.begin(); it != providedServices.end(); ++it) {
136  std::shared_ptr<BaseProvidedService> provide = *it;
137  if (provide->getService() == static_cast<const void*>(svc)) {
138  providedServices.erase(it);
139  break;
140  }
141  }
142  return *this;
143 }
144 
145 template<class T>
146 template<class I>
148  auto dep = std::shared_ptr<ServiceDependency<T,I>> {new ServiceDependency<T,I>(cComponent(), name)};
149  std::lock_guard<std::mutex> lck{mutex};
150  dependencies.push_back(dep);
151  dep->setComponentInstance(&getInstance());
152  return *dep;
153 }
154 
155 template<class T>
156 template<class I>
158  celix_component_removeServiceDependency(cComponent(), dep.cServiceDependency());
159  std::lock_guard<std::mutex> lck{mutex};
160  this->dependencies.erase(std::remove(this->dependencies.begin(), this->dependencies.end(), dep));
161  return *this;
162 }
163 
164 template<class T>
165 template<typename I>
167  auto dep = std::shared_ptr<CServiceDependency<T,I>> {new CServiceDependency<T,I>(cComponent(), name)};
168  std::lock_guard<std::mutex> lck{mutex};
169  dependencies.push_back(dep);
170  dep->setComponentInstance(&getInstance());
171  return *dep;
172 }
173 
174 template<class T>
175 template<typename I>
177  celix_component_removeServiceDependency(cComponent(), dep.cServiceDependency());
178  std::lock_guard<std::mutex> lck{mutex};
179  this->dependencies.erase(std::remove(this->dependencies.begin(), this->dependencies.end(), dep));
180  return *this;
181 }
182 
183 template<class T>
184 std::shared_ptr<Component<T>> Component<T>::create(celix_bundle_context_t *context, celix_dependency_manager_t* cDepMan, std::string name, std::string uuid) {
185  std::string cmpName = celix::cmpTypeName<T>(name);
186  return std::shared_ptr<Component<T>>{new Component<T>(context, cDepMan, std::move(cmpName), std::move(uuid)), [](Component<T>* cmp){
187  //Using a callback of async destroy to ensure that the cmp instance is still exist while the
188  //dm component is async disabled and destroyed.
189  auto cb = [](void *data) {
190  auto* c = static_cast<Component<T>*>(data);
191  delete c;
192  };
193 
194  if (cmp->cmpAddedToDepMan) {
195  celix_dependencyManager_removeAsync(cmp->cDepMan, cmp->cCmp, cmp, cb);
196  } else {
197  celix_dmComponent_destroyAsync(cmp->cCmp, cmp, cb);
198  }
199  }};
200 }
201 
202 template<class T>
203 bool Component<T>::isValid() const {
204  return this->bundleContext() != nullptr;
205 }
206 
207 
208 template<typename T>
209 static
210 typename std::enable_if<std::is_default_constructible<T>::value, T*>::type
211 createInstance() {
212  return new T{};
213 }
214 
215 template<typename T>
216 static
217 typename std::enable_if<!std::is_default_constructible<T>::value, T*>::type
218 createInstance() {
219  return nullptr;
220 }
221 
222 template<class T>
224  std::lock_guard<std::mutex> lck{instanceMutex};
225  if (!valInstance.empty()) {
226  return valInstance.front();
227  } else if (sharedInstance) {
228  return *sharedInstance;
229  } else if (instance) {
230  return *instance;
231  } else {
232  T* newInstance = createInstance<T>(); //uses SFINAE to detect if default ctor exists
233  instance = std::unique_ptr<T>{newInstance};
234  return *instance;
235  }
236 }
237 
238 template<class T>
239 Component<T>& Component<T>::setInstance(std::shared_ptr<T> inst) {
240  std::lock_guard<std::mutex> lck{instanceMutex};
241  this->valInstance.clear();
242  this->instance = std::unique_ptr<T> {nullptr};
243  this->sharedInstance = std::move(inst);
244  return *this;
245 }
246 
247 template<class T>
248 Component<T>& Component<T>::setInstance(std::unique_ptr<T>&& inst) {
249  std::lock_guard<std::mutex> lck{instanceMutex};
250  this->valInstance.clear();
251  this->sharedInstance = std::shared_ptr<T> {nullptr};
252  this->instance = std::move(inst);
253  return *this;
254 }
255 
256 template<class T>
258  std::lock_guard<std::mutex> lck{instanceMutex};
259  this->instance = std::unique_ptr<T> {nullptr};
260  this->sharedInstance = std::shared_ptr<T> {nullptr};
261  this->valInstance.clear();
262  this->valInstance.push_back(std::forward<T>(inst));
263  return *this;
264 }
265 
266 template<class T>
267 int Component<T>::invokeLifecycleMethod(const std::string& methodName, void (T::*lifecycleMethod)()) {
268  try {
269  if (lifecycleMethod != nullptr) {
270  T& inst = getInstance();
271  (inst.*lifecycleMethod)();
272  }
273  } catch (const std::exception& e) {
274  celix_bundleContext_log(context, CELIX_LOG_LEVEL_ERROR, "Error invoking %s for component %s (uuid=%s). Exception: %s",
275  methodName.c_str(),
276  cmpName.c_str(),
277  cmpUUID.c_str(),
278  e.what());
279  return -1;
280  } catch (...) {
281  celix_bundleContext_log(context, CELIX_LOG_LEVEL_ERROR, "Error invoking %s for component %s (uuid=%s).",
282  methodName.c_str(),
283  cmpName.c_str(),
284  cmpUUID.c_str());
285  return -1;
286  }
287  return 0;
288 }
289 
290 template<class T>
292  void (T::*init)(),
293  void (T::*start)(),
294  void (T::*stop)(),
295  void (T::*deinit)() ) {
296 
297  this->initFp = init;
298  this->startFp = start;
299  this->stopFp = stop;
300  this->deinitFp = deinit;
301 
302 
303  int (*cInit)(void *) = [](void *handle) {
304  Component<T>* cmp = (Component<T>*)(handle);
305  return cmp->invokeLifecycleMethod("init", cmp->initFp);
306  };
307  int (*cStart)(void *) = [](void *handle) {
308  Component<T>* cmp = (Component<T>*)(handle);
309  return cmp->invokeLifecycleMethod("start", cmp->startFp);
310  };
311  int (*cStop)(void *) = [](void *handle) {
312  Component<T>* cmp = (Component<T>*)(handle);
313  return cmp->invokeLifecycleMethod("stop", cmp->stopFp);
314  };
315  int (*cDeinit)(void *) = [](void *handle) {
316  Component<T>* cmp = (Component<T>*)(handle);
317  return cmp->invokeLifecycleMethod("deinit", cmp->deinitFp);
318  };
319 
320  celix_dmComponent_setCallbacks(this->cComponent(), cInit, cStart, cStop, cDeinit);
321 
322  return *this;
323 }
324 
325 template<class T>
327  int (T::*init)(),
328  int (T::*start)(),
329  int (T::*stop)(),
330  int (T::*deinit)() ) {
331 
332  this->initFpNoExc = init;
333  this->startFpNoExc = start;
334  this->stopFpNoExc = stop;
335  this->deinitFpNoExc = deinit;
336 
337  int (*cInit)(void *) = [](void *handle) {
338  Component<T>* cmp = (Component<T>*)(handle);
339  T* inst = &cmp->getInstance();
340  int (T::*fp)() = cmp->initFpNoExc;
341  if (fp != nullptr) {
342  return (inst->*fp)();
343  }
344  return 0;
345  };
346  int (*cStart)(void *) = [](void *handle) {
347  Component<T>* cmp = (Component<T>*)(handle);
348  T* inst = &cmp->getInstance();
349  int (T::*fp)() = cmp->startFpNoExc;
350  if (fp != nullptr) {
351  return (inst->*fp)();
352  }
353  return 0;
354  };
355  int (*cStop)(void *) = [](void *handle) {
356  Component<T>* cmp = (Component<T>*)(handle);
357  T* inst = &cmp->getInstance();
358  int (T::*fp)() = cmp->stopFpNoExc;
359  if (fp != nullptr) {
360  return (inst->*fp)();
361  }
362  return 0;
363  };
364  int (*cDeinit)(void *) = [](void *handle) {
365  Component<T>* cmp = (Component<T>*)(handle);
366  T* inst = &cmp->getInstance();
367  int (T::*fp)() = cmp->deinitFpNoExc;
368  if (fp != nullptr) {
369  return (inst->*fp)();
370  }
371  return 0;
372  };
373 
374  celix_dmComponent_setCallbacks(this->cComponent(), cInit, cStart, cStop, cDeinit);
375 
376  return *this;
377 }
378 
379 template<class T>
381  celix_dmComponent_setCallbacks(this->cComponent(), nullptr, nullptr, nullptr, nullptr);
382  return *this;
383 }
384 
385 template<class T>
386 Component<T>& Component<T>::addContext(std::shared_ptr<void> context) {
387  std::lock_guard<std::mutex> lock{mutex};
388  componentContexts.template emplace_back(std::move(context));
389  return *this;
390 }
391 
392 template<typename T>
394  runBuild();
395  wait();
396  return *this;
397 }
398 
399 template<typename T>
401  runBuild();
402  return *this;
403 }
404 
405 template<class T>
406 template<class I>
407 ProvidedService<T, I> &Component<T>::createProvidedCService(I *svcPtr, std::string serviceName) {
408  std::shared_ptr<I> svc{svcPtr, [](I*){/*nop*/}};
409  auto provide = std::make_shared<ProvidedService<T,I>>(cComponent(), serviceName, std::move(svc), false);
410  std::lock_guard<std::mutex> lck{mutex};
411  providedServices.push_back(provide);
412  return *provide;
413 }
414 
415 template<class T>
416 template<class I>
418  static_assert(std::is_base_of<I,T>::value, "Component T must implement Interface I");
419  if (serviceName.empty()) {
420  serviceName = typeName<I>();
421  }
422  if (serviceName.empty()) {
423  std::cerr << "Cannot add interface, because type name could not be inferred. function: '" << __PRETTY_FUNCTION__ << "'\n";
424  }
425 
426  I* svcPtr = &this->getInstance();
427  std::shared_ptr<I> svc{svcPtr, [](I*){/*nop*/}};
428  auto provide = std::make_shared<ProvidedService<T,I>>(cComponent(), serviceName, std::move(svc), true);
429  auto svcVersion = celix::typeVersion<I>();
430  if (!svcVersion.empty()) {
431  provide->addProperty(celix::SERVICE_VERSION, std::move(svcVersion));
432  }
433  std::lock_guard<std::mutex> lck{mutex};
434  providedServices.push_back(provide);
435  return *provide;
436 }
437 
438 template<class T>
439 template<class I>
440 ProvidedService<T, I>& Component<T>::createUnassociatedProvidedService(std::shared_ptr<I> svc, std::string serviceName) {
441  if (serviceName.empty()) {
442  serviceName = typeName<I>();
443  }
444  if (serviceName.empty()) {
445  std::cerr << "Cannot add interface, because type name could not be inferred. function: '" << __PRETTY_FUNCTION__ << "'\n";
446  }
447 
448  auto provide = std::make_shared<ProvidedService<T,I>>(cComponent(), serviceName, std::move(svc), true);
449  auto svcVersion = celix::typeVersion<I>();
450  if (!svcVersion.empty()) {
451  provide->addProperty(celix::SERVICE_VERSION, std::move(svcVersion));
452  }
453  std::lock_guard<std::mutex> lck{mutex};
454  providedServices.push_back(provide);
455  return *provide;
456 }
457 
celix::dm::ProvidedService
Definition: ProvidedService.h:57
celix::dm::CServiceDependency
A service dependency for a component.
Definition: ServiceDependency.h:134
celix_dmComponent_setCallbacks
celix_status_t celix_dmComponent_setCallbacks(celix_dm_component_t *component, celix_dm_cmp_lifecycle_fpt init, celix_dm_cmp_lifecycle_fpt start, celix_dm_cmp_lifecycle_fpt stop, celix_dm_cmp_lifecycle_fpt deinit)
celix::dm::BaseComponent::~BaseComponent
virtual ~BaseComponent() noexcept
celix
Definition: Bundle.h:26
celix_dmComponent_getComponentInfo
celix_status_t celix_dmComponent_getComponentInfo(celix_dm_component_t *component, celix_dm_component_info_t **infoOut)
celix::dm::operator<<
std::ostream & operator<<(std::ostream &out, const BaseComponent &cmp)
Definition: Component_Impl.h:54
celix::dm::BaseComponent::context
celix_bundle_context_t * context
Definition: Component.h:143
celix::dm::BaseComponent::cmpAddedToDepMan
std::atomic< bool > cmpAddedToDepMan
Definition: Component.h:149
celix::dm::BaseComponent::dependencies
std::vector< std::shared_ptr< BaseServiceDependency > > dependencies
Definition: Component.h:152
celix::SERVICE_VERSION
constexpr const char *const SERVICE_VERSION
Service property (named "service.version") specifying the optional version of a service.
Definition: Constants.h:106
celix::dm::BaseComponent::runBuild
void runBuild()
Definition: Component_Impl.h:22
celix::dm::Component
Definition: Component.h:163
celix_dmComponent_printComponentInfo
void celix_dmComponent_printComponentInfo(celix_dm_component_info_t *info, bool printFullInfo, bool useAnsiColors, FILE *stream)
celix_dmComponent_destroyAsync
void celix_dmComponent_destroyAsync(celix_dm_component_t *cmp, void *doneData, void(*doneCallback)(void *))
celix::dm::BaseComponent::cComponent
celix_dm_component_t * cComponent() const
Definition: Component.h:73
celix::dm::TypedServiceDependency::setComponentInstance
void setComponentInstance(T *cmp)
Definition: ServiceDependency.h:124
celix::dm::BaseServiceDependency::cServiceDependency
celix_dm_service_dependency_t * cServiceDependency() const
Definition: ServiceDependency.h:89
celix::dm::BaseComponent::cCmp
celix_dm_component_t * cCmp
Definition: Component.h:145
celix::dm::BaseComponent::providedServices
std::vector< std::shared_ptr< BaseProvidedService > > providedServices
Definition: Component.h:153
celix_dmComponent_destroyComponentInfo
void celix_dmComponent_destroyComponentInfo(dm_component_info_pt info)
celix_dm_component_info_struct
Definition: celix_dm_info.h:54
celix::dm::BaseComponent::cDepMan
celix_dependency_manager_t * cDepMan
Definition: Component.h:144
celix::dm
Definition: Component.h:41
celix_bundleContext_log
void celix_bundleContext_log(const celix_bundle_context_t *ctx, celix_log_level_e level, const char *format,...)
Logs a message to Celix framework logger with the provided log level.
celix::dm::Properties
celix::Properties Properties
Definition: Properties.h:25
celix_dmComponent_removeInterface
celix_status_t celix_dmComponent_removeInterface(celix_dm_component_t *component, const void *service)
celix::dm::BaseComponent
Definition: Component.h:57
celix::dm::ServiceDependency
A service dependency for a component.
Definition: ServiceDependency.h:266
celix::dm::BaseComponent::mutex
std::mutex mutex
Definition: Component.h:151
celix::dm::Component::getInstance
T & getInstance()
Definition: Component_Impl.h:223