Keyple Service Resource C++ Library - 3.1.1
Component of the Keyple C++ middleware
CardResourceServiceAdapter.cpp
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (c) 2025 Calypso Networks Association https://calypsonet.org/ *
3 * *
4 * See the NOTICE file(s) distributed with this work for additional *
5 * information regarding copyright ownership. *
6 * *
7 * This program and the accompanying materials are made available under the *
8 * terms of the Eclipse Public License 2.0 which is available at *
9 * http://www.eclipse.org/legal/epl-2.0 *
10 * *
11 * SPDX-License-Identifier: EPL-2.0 *
12 ******************************************************************************/
13
14#include "keyple/core/service/resource/CardResourceServiceAdapter.hpp"
15
16#include <sstream>
17
18#include "keyple/core/service/ObservablePlugin.hpp"
19#include "keyple/core/service/SmartCardServiceProvider.hpp"
20#include "keyple/core/service/resource/CardProfileManagerAdapter.hpp"
21#include "keyple/core/util/HexUtil.hpp"
22#include "keyple/core/util/KeypleAssert.hpp"
23#include "keyple/core/util/cpp/System.hpp"
24#include "keyple/core/util/cpp/exception/IllegalStateException.hpp"
25#include "keypop/reader/CardReader.hpp"
26
27namespace keyple {
28namespace core {
29namespace service {
30namespace resource {
31
32using keyple::core::service::ObservablePlugin;
33using keyple::core::service::SmartCardServiceProvider;
34using keyple::core::util::HexUtil;
35using keyple::core::util::cpp::System;
36using keyple::core::util::cpp::exception::IllegalStateException;
37using keypop::reader::CardReader;
38
39std::shared_ptr<CardResourceServiceAdapter>
40 CardResourceServiceAdapter::mInstance;
41
42std::shared_ptr<CardResourceServiceAdapter>
43CardResourceServiceAdapter::getInstance()
44{
45 if (mInstance == nullptr) {
46 mInstance = std::make_shared<CardResourceServiceAdapter>();
47 }
48
49 return mInstance;
50}
51
52const std::string
53CardResourceServiceAdapter::getCardResourceInfo(
54 const std::shared_ptr<CardResource> cardResource)
55{
56 std::stringstream ss;
57
58 ss << *cardResource;
59
60 return ss.str();
61}
62
63std::shared_ptr<ReaderManagerAdapter>
64CardResourceServiceAdapter::getReaderManager(
65 const std::shared_ptr<CardReader> reader) const
66{
67 const auto it = mReaderToReaderManagerMap.find(reader);
68 if (it != mReaderToReaderManagerMap.end()) {
69 return it->second;
70 }
71
72 return nullptr;
73}
74
75void
76CardResourceServiceAdapter::registerPoolCardResource(
77 std::shared_ptr<CardResource> cardResource,
78 std::shared_ptr<PoolPlugin> poolPlugin)
79{
80 mCardResourceToPoolPluginMap.insert({cardResource, poolPlugin});
81}
82
83void
84CardResourceServiceAdapter::configure(
85 std::shared_ptr<CardResourceServiceConfiguratorAdapter> configurator)
86{
87 mLogger->info("Applying new card resource service configuration\n");
88
89 if (mIsStarted) {
90 stop();
91 mConfigurator = configurator;
92 start();
93 } else {
94 mConfigurator = configurator;
95 }
96
97 mLogger->info("New card resource service configuration applied\n");
98}
99
100std::shared_ptr<CardResourceServiceConfigurator>
101CardResourceServiceAdapter::getConfigurator() const
102{
103 return std::make_shared<CardResourceServiceConfiguratorAdapter>();
104}
105
106void
107CardResourceServiceAdapter::start()
108{
109 if (mConfigurator == nullptr) {
110 throw IllegalStateException(
111 "The card resource service is not configured");
112 }
113
114 if (mIsStarted) {
115 stop();
116 }
117
118 mLogger->info("Starting card resource service\n");
119
120 initializeReaderManagers();
121 initializeCardProfileManagers();
122 removeUnusedReaderManagers();
123 startMonitoring();
124 mIsStarted = true;
125
126 mLogger->info("Card resource service started\n");
127}
128
129void
130CardResourceServiceAdapter::stop()
131{
132 mIsStarted = false;
133
134 stopMonitoring();
135
136 mReaderToReaderManagerMap.clear();
137 mCardProfileNameToCardProfileManagerMap.clear();
138 mCardResourceToPoolPluginMap.clear();
139 mPluginToObservableReadersMap.clear();
140
141 mLogger->info("Card resource service stopped\n");
142}
143
144std::shared_ptr<CardResource>
145CardResourceServiceAdapter::getCardResource(
146 const std::string& cardResourceProfileName) const
147{
148 mLogger->debug(
149 "Searching available card resource [profile=%]\n",
150 cardResourceProfileName);
151
152 if (!mIsStarted) {
153 throw IllegalStateException("Card resource service not started");
154 }
155
156 Assert::getInstance().notEmpty(
157 cardResourceProfileName, "cardResourceProfileName");
158
159 std::shared_ptr<CardProfileManagerAdapter> cardProfileManager = nullptr;
160 const auto& it
161 = mCardProfileNameToCardProfileManagerMap.find(cardResourceProfileName);
162 if (it != mCardProfileNameToCardProfileManagerMap.end()) {
163 cardProfileManager = it->second;
164 }
165
166 Assert::getInstance().notNull(
167 cardProfileManager, "cardResourceProfileName");
168
169 std::shared_ptr<CardResource> cardResource
170 = cardProfileManager->getCardResource();
171
172 mLogger->debug(
173 "Card resource found [cardResource=%]\n",
174 getCardResourceInfo(cardResource));
175
176 return cardResource;
177}
178
179void
180CardResourceServiceAdapter::releaseCardResource(
181 std::shared_ptr<CardResource> cardResource)
182{
183 mLogger->debug(
184 "Releasing card resource [cardResource=%]\n",
185 getCardResourceInfo(cardResource));
186
187 if (!mIsStarted) {
188 throw IllegalStateException("Card resource service not started");
189 }
190
191 Assert::getInstance().notNull(cardResource, "cardResource");
192
193 /* For regular or pool plugin ? */
194 std::shared_ptr<ReaderManagerAdapter> readerManager = nullptr;
195
196 const auto reader = cardResource->getReader();
197 if (reader == nullptr) {
198 throw IllegalArgumentException("Invalid reader");
199 }
200
201 const auto it = mReaderToReaderManagerMap.find(reader);
202 if (it != mReaderToReaderManagerMap.end()) {
203 readerManager = it->second;
204 readerManager->unlock();
205 } else {
206 std::shared_ptr<PoolPlugin> poolPlugin = nullptr;
207 const auto itt = mCardResourceToPoolPluginMap.find(cardResource);
208 if (itt != mCardResourceToPoolPluginMap.end()) {
209 poolPlugin = itt->second;
210 mCardResourceToPoolPluginMap.erase(cardResource);
211 poolPlugin->releaseReader(reader);
212 }
213 }
214
215 mLogger->debug("Card resource released\n");
216}
217
218void
219CardResourceServiceAdapter::removeCardResource(
220 std::shared_ptr<CardResource> cardResource)
221{
222 releaseCardResource(cardResource);
223
224 /* For regular plugin ? */
225 std::shared_ptr<ReaderManagerAdapter> readerManager = nullptr;
226
227 const auto r
228 = std::dynamic_pointer_cast<CardReader>(cardResource->getReader());
229 if (r == nullptr) {
230 throw IllegalArgumentException("Invalid reader");
231 }
232
233 const auto it = mReaderToReaderManagerMap.find(r);
234 if (it != mReaderToReaderManagerMap.end()) {
235 readerManager = it->second;
236 readerManager->removeCardResource(cardResource);
237
238 for (auto& pair : mCardProfileNameToCardProfileManagerMap) {
239 pair.second->removeCardResource(cardResource);
240 }
241 }
242
243 mLogger->info(
244 "Card resource removed [cardResource=%]\n",
245 getCardResourceInfo(cardResource));
246}
247
248void
249CardResourceServiceAdapter::onPluginEvent(
250 const std::shared_ptr<PluginEvent> pluginEvent)
251{
252 if (!mIsStarted) {
253 return;
254 }
255
256 std::shared_ptr<Plugin> plugin
257 = SmartCardServiceProvider::getService()->getPlugin(
258 pluginEvent->getPluginName());
259
260 if (pluginEvent->getType() == PluginEvent::Type::READER_CONNECTED) {
261 for (const std::string& readerName : pluginEvent->getReaderNames()) {
262 /* Get the new reader from the plugin because it is not yet
263 * registered in the service */
264 std::shared_ptr<CardReader> reader = plugin->getReader(readerName);
265 if (reader != nullptr) {
266 const std::lock_guard<std::mutex> lock(mMutex);
267 onReaderConnected(reader, plugin);
268 }
269 }
270 } else {
271 for (const std::string& readerName : pluginEvent->getReaderNames()) {
272 /*
273 * Get the reader back from the service because it is no longer
274 * registered in the plugin.
275 */
276 std::shared_ptr<CardReader> reader = getReader(readerName);
277 if (reader != nullptr) {
278 /* The reader is registered in the service */
279 const std::lock_guard<std::mutex> lock(mMutex);
280 onReaderDisconnected(reader, plugin);
281 }
282 }
283 }
284}
285
286void
287CardResourceServiceAdapter::onReaderEvent(
288 const std::shared_ptr<CardReaderEvent> readerEvent)
289{
290 if (!mIsStarted) {
291 return;
292 }
293
294 std::shared_ptr<CardReader> reader
295 = getReader(readerEvent->getReaderName());
296 if (reader != nullptr) {
297 /* The reader is registered in the service */
298 const std::lock_guard<std::mutex> lock(mMutex);
299 const auto it = mReaderToReaderManagerMap.find(reader);
300 if (it != mReaderToReaderManagerMap.end()) {
301 onReaderEvent(readerEvent, it->second);
302 }
303 }
304}
305
306void
307CardResourceServiceAdapter::initializeReaderManagers()
308{
309 for (const auto& plugin : mConfigurator->getPlugins()) {
310 for (const auto& reader : plugin->getReaders()) {
311 registerReader(reader, plugin);
312 }
313 }
314}
315
316std::shared_ptr<ReaderManagerAdapter>
317CardResourceServiceAdapter::registerReader(
318 std::shared_ptr<CardReader> reader, std::shared_ptr<Plugin> plugin)
319{
320 /*
321 * Get the reader configurator if a monitoring is requested for this reader
322 */
323 std::shared_ptr<ReaderConfiguratorSpi> readerConfiguratorSpi = nullptr;
324
325 const auto& configuredPlugins = mConfigurator->getConfiguredPlugins();
326 const auto it = std::find_if(
327 configuredPlugins.begin(),
328 configuredPlugins.end(),
329 [plugin](const std::shared_ptr<ConfiguredPlugin>& configuredPlugin) {
330 return configuredPlugin->getPlugin() == plugin;
331 });
332
333 readerConfiguratorSpi = it != configuredPlugins.end()
334 ? (*it)->getReaderConfiguratorSpi()
335 : nullptr;
336
337 auto readerManager = std::make_shared<ReaderManagerAdapter>(
338 reader,
339 plugin,
340 readerConfiguratorSpi,
341 mConfigurator->getUsageTimeoutMillis());
342
343 mReaderToReaderManagerMap.insert({reader, readerManager});
344
345 const auto observable
346 = std::dynamic_pointer_cast<ObservableCardReader>(reader);
347 if (observable) {
348 const auto _it = mPluginToObservableReadersMap.find(plugin);
349 if (_it != mPluginToObservableReadersMap.end()) {
350 /* Add element to existing vector */
351 _it->second.push_back(observable);
352 } else {
353 /* Create a new vector and add an entry to map */
354 std::vector<std::shared_ptr<ObservableCardReader>>
355 usedObservableReaders;
356 usedObservableReaders.push_back(observable);
357 mPluginToObservableReadersMap.insert(
358 {plugin, usedObservableReaders});
359 }
360 }
361
362 return readerManager;
363}
364
365void
366CardResourceServiceAdapter::initializeCardProfileManagers()
367{
368 for (const auto& profile :
369 mConfigurator->getCardResourceProfileConfigurators()) {
370 mCardProfileNameToCardProfileManagerMap.insert(
371 {profile->getProfileName(),
372 std::make_shared<CardProfileManagerAdapter>(
373 profile, mConfigurator)});
374 }
375}
376
377void
378CardResourceServiceAdapter::removeUnusedReaderManagers()
379{
380 std::vector<std::shared_ptr<ReaderManagerAdapter>> readerManagers;
381 for (const auto& pair : mReaderToReaderManagerMap) {
382 readerManagers.push_back(pair.second);
383 }
384
385 for (const auto& readerManager : readerManagers) {
386 if (!readerManager->isActive()) {
387 unregisterReader(
388 readerManager->getReader(), readerManager->getPlugin());
389 }
390 }
391}
392
393void
394CardResourceServiceAdapter::unregisterReader(
395 std::shared_ptr<CardReader> reader, std::shared_ptr<Plugin> plugin)
396{
397 mReaderToReaderManagerMap.erase(reader);
398
399 const auto it = mPluginToObservableReadersMap.find(plugin);
400 auto observableCardReader
401 = std::dynamic_pointer_cast<ObservableCardReader>(reader);
402
403 if (it != mPluginToObservableReadersMap.end()
404 && observableCardReader != nullptr) {
405 observableCardReader->removeObserver(shared_from_this());
406 it->second.erase(
407 std::remove(
408 it->second.begin(), it->second.end(), observableCardReader),
409 it->second.end());
410 }
411}
412
413void
414CardResourceServiceAdapter::startMonitoring()
415{
416 for (auto& configuredPlugin : mConfigurator->getConfiguredPlugins()) {
417 auto observable = std::dynamic_pointer_cast<ObservablePlugin>(
418 configuredPlugin->getPlugin());
419 if (configuredPlugin->isWithPluginMonitoring()
420 && observable != nullptr) {
421 mLogger->info(
422 "Reader monitoring start requested [plugin=%]\n",
423 configuredPlugin->getPlugin()->getName());
424 startPluginObservation(configuredPlugin);
425 }
426
427 const auto it
428 = mPluginToObservableReadersMap.find(configuredPlugin->getPlugin());
429 if (configuredPlugin->isWithReaderMonitoring()
430 && it != mPluginToObservableReadersMap.end()) {
431 for (auto& reader : it->second) {
432 mLogger->info(
433 "Card monitoring start requested [reader=%]\n",
434 reader->getName());
435 startReaderObservation(reader, configuredPlugin);
436 }
437 }
438 }
439}
440
441void
442CardResourceServiceAdapter::stopMonitoring()
443{
444 for (auto& configuredPlugin : mConfigurator->getConfiguredPlugins()) {
445 auto observable = std::dynamic_pointer_cast<ObservablePlugin>(
446 configuredPlugin->getPlugin());
447 if (configuredPlugin->isWithPluginMonitoring()
448 && observable != nullptr) {
449 mLogger->info(
450 "Reader monitoring stopped [plugin=%]\n",
451 configuredPlugin->getPlugin()->getName());
452 std::dynamic_pointer_cast<ObservablePlugin>(
453 configuredPlugin->getPlugin())
454 ->removeObserver(shared_from_this());
455 }
456
457 const auto it
458 = mPluginToObservableReadersMap.find(configuredPlugin->getPlugin());
459 if (configuredPlugin->isWithReaderMonitoring()
460 && it != mPluginToObservableReadersMap.end()) {
461 for (auto& reader : it->second) {
462 mLogger->info(
463 "Card monitoring stopped [reader=%]\n", reader->getName());
464 reader->removeObserver(shared_from_this());
465 }
466 }
467 }
468}
469
470std::shared_ptr<CardReader>
471CardResourceServiceAdapter::getReader(const std::string& readerName) const
472{
473 auto it = std::find_if(
474 mReaderToReaderManagerMap.begin(),
475 mReaderToReaderManagerMap.end(),
476 [readerName](const std::pair<
477 std::shared_ptr<CardReader>,
478 std::shared_ptr<ReaderManagerAdapter>>& pair) {
479 return pair.first->getName() == readerName;
480 });
481
482 return (it != mReaderToReaderManagerMap.end()) ? (*it).first : nullptr;
483}
484
485void
486CardResourceServiceAdapter::onReaderConnected(
487 std::shared_ptr<CardReader> reader, std::shared_ptr<Plugin> plugin)
488{
489 std::shared_ptr<ReaderManagerAdapter> readerManager
490 = registerReader(reader, plugin);
491 for (auto pair : mCardProfileNameToCardProfileManagerMap) {
492 pair.second->onReaderConnected(readerManager);
493 }
494
495 if (readerManager->isActive()) {
496 startMonitoring(reader, plugin);
497 } else {
498 unregisterReader(reader, plugin);
499 }
500}
501
502void
503CardResourceServiceAdapter::startMonitoring(
504 std::shared_ptr<CardReader> reader, std::shared_ptr<Plugin> plugin)
505{
506 auto observable = std::dynamic_pointer_cast<ObservableCardReader>(reader);
507 if (observable != nullptr) {
508 for (auto& configuredPlugin : mConfigurator->getConfiguredPlugins()) {
509 if (configuredPlugin->getPlugin() == plugin
510 && configuredPlugin->isWithReaderMonitoring()) {
511 mLogger->info(
512 "Card monitoring start requested [reader=%]\n",
513 reader->getName());
514 startReaderObservation(observable, configuredPlugin);
515 }
516 }
517 }
518}
519
520void
521CardResourceServiceAdapter::startPluginObservation(
522 std::shared_ptr<ConfiguredPlugin> configuredPlugin)
523{
524 auto observablePlugin = std::dynamic_pointer_cast<ObservablePlugin>(
525 configuredPlugin->getPlugin());
526 observablePlugin->setPluginObservationExceptionHandler(
527 configuredPlugin->getPluginObservationExceptionHandlerSpi());
528 observablePlugin->addObserver(shared_from_this());
529}
530
531void
532CardResourceServiceAdapter::startReaderObservation(
533 std::shared_ptr<ObservableCardReader> observableReader,
534 std::shared_ptr<ConfiguredPlugin> configuredPlugin)
535{
536 observableReader->setReaderObservationExceptionHandler(
537 configuredPlugin->getReaderObservationExceptionHandlerSpi());
538 observableReader->addObserver(shared_from_this());
539 observableReader->startCardDetection(
540 ObservableCardReader::DetectionMode::REPEATING);
541}
542
543void
544CardResourceServiceAdapter::onReaderDisconnected(
545 std::shared_ptr<CardReader> reader, std::shared_ptr<Plugin> plugin)
546{
547 const auto it = mReaderToReaderManagerMap.find(reader);
548 if (it != mReaderToReaderManagerMap.end()) {
549 mLogger->debug(
550 "Removing disconnected reader and all associated card resources " \
551 "[reader=%]\n",
552 reader->getName());
553
554 onCardRemoved(it->second);
555 unregisterReader(reader, plugin);
556 }
557}
558
559void
560CardResourceServiceAdapter::onReaderEvent(
561 const std::shared_ptr<CardReaderEvent> readerEvent,
562 std::shared_ptr<ReaderManagerAdapter> readerManager)
563{
564 if (readerEvent->getType() == CardReaderEvent::Type::CARD_INSERTED
565 || readerEvent->getType() == CardReaderEvent::Type::CARD_MATCHED) {
566 mLogger->info(
567 "Creating new card resources matching the new card inserted " \
568 "[reader=%]\n",
569 readerManager->getReader()->getName());
570
571 onCardInserted(readerManager);
572
573 } else {
574 mLogger->debug(
575 "Removing all card resources caused by a card removal or reader " \
576 "unregistration [reader=%]\n",
577 readerManager->getReader()->getName());
578
579 onCardRemoved(readerManager);
580 }
581}
582
583void
584CardResourceServiceAdapter::onCardInserted(
585 std::shared_ptr<ReaderManagerAdapter> readerManager)
586{
587 for (auto& pair : mCardProfileNameToCardProfileManagerMap) {
588 pair.second->onCardInserted(readerManager);
589 }
590}
591
592void
593CardResourceServiceAdapter::onCardRemoved(
594 std::shared_ptr<ReaderManagerAdapter> readerManager)
595{
596 const std::vector<std::shared_ptr<CardResourceAdapter>>&
597 cardResourcesToRemove
598 = readerManager->getCardResources();
599
600 for (const auto& cardResource : cardResourcesToRemove) {
601 removeCardResource(cardResource);
602 }
603}
604
605} /* namespace resource */
606} /* namespace service */
607} /* namespace core */
608} /* namespace keyple */