Keyple Service C++ Library - 3.4.1
Component of the Keyple C++ middleware
SmartCardServiceAdapter.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/SmartCardServiceAdapter.hpp"
15
16#include <cstdlib>
17#include <memory>
18#include <string>
19#include <utility>
20#include <vector>
21
22#include "keyple/core/common/CommonApiProperties.hpp"
23#include "keyple/core/plugin/PluginApiProperties.hpp"
24#include "keyple/core/plugin/PluginIOException.hpp"
25#include "keyple/core/plugin/spi/AutonomousObservablePluginSpi.hpp"
26#include "keyple/core/plugin/spi/ObservablePluginSpi.hpp"
27#include "keyple/core/plugin/spi/PluginSpi.hpp"
28#include "keyple/core/plugin/spi/PoolPluginSpi.hpp"
29#include "keyple/core/service/AutonomousObservableLocalPluginAdapter.hpp"
30#include "keyple/core/service/KeyplePluginException.hpp"
31#include "keyple/core/service/LocalPoolPluginAdapter.hpp"
32#include "keyple/core/service/ObservableLocalPluginAdapter.hpp"
33#include "keyple/core/service/ReaderApiFactoryAdapter.hpp"
34#include "keyple/core/util/KeypleAssert.hpp"
35#include "keyple/core/util/cpp/KeypleStd.hpp"
36#include "keyple/core/util/cpp/StringUtils.hpp"
37#include "keyple/core/util/cpp/exception/IllegalArgumentException.hpp"
38#include "keyple/core/util/cpp/exception/IllegalStateException.hpp"
39#include "keyple/core/util/cpp/exception/PatternSyntaxException.hpp"
40#include "keypop/card/CardApiProperties.hpp"
41#include "keypop/reader/ReaderApiProperties.hpp"
42
43namespace keyple {
44namespace core {
45namespace service {
46
47using keyple::core::common::CommonApiProperties_VERSION;
48using keyple::core::plugin::PluginApiProperties_VERSION;
49using keyple::core::plugin::PluginIOException;
50using keyple::core::plugin::spi::AutonomousObservablePluginSpi;
51using keyple::core::plugin::spi::ObservablePluginSpi;
52using keyple::core::plugin::spi::PluginSpi;
53using keyple::core::plugin::spi::PoolPluginSpi;
54using keyple::core::service::AutonomousObservableLocalPluginAdapter;
55using keyple::core::service::KeyplePluginException;
56using keyple::core::service::LocalPoolPluginAdapter;
57using keyple::core::service::ReaderApiFactoryAdapter;
58using keyple::core::util::Assert;
59using keyple::core::util::cpp::StringUtils;
60using keyple::core::util::cpp::exception::IllegalArgumentException;
61using keyple::core::util::cpp::exception::IllegalStateException;
62using keyple::core::util::cpp::exception::PatternSyntaxException;
63using keypop::card::CardApiProperties_VERSION;
64using keypop::reader::ReaderApiProperties_VERSION;
65
66const std::string SmartCardServiceAdapter::MSG_VERSION_MISMATCH_DETECTED
67 = "Version mismatch detected: % '%' uses '%' version '%' (expected '%'). "
68 "Compatibility issues "
69 "may arise\n";
70
71std::shared_ptr<SmartCardServiceAdapter> SmartCardServiceAdapter::mInstance;
72
73SmartCardServiceAdapter::SmartCardServiceAdapter()
74{
75 const char* value = std::getenv("isAutomaticStatusCodeHandlingEnabled");
76 if (value != nullptr
77 && StringUtils::tolower(std::string(value)) == "false") {
78 mIsAutomaticStatusCodeHandlingEnabled = false;
79 mLogger->warn("Automatic status code handling is disabled\n");
80 }
81}
82
83bool
84SmartCardServiceAdapter::isAutomaticStatusCodeHandlingEnabled() const
85{
86 return mIsAutomaticStatusCodeHandlingEnabled;
87}
88
89std::shared_ptr<SmartCardServiceAdapter>
90SmartCardServiceAdapter::getInstance()
91{
92 if (mInstance == nullptr) {
93 mInstance = std::shared_ptr<SmartCardServiceAdapter>(
94 new SmartCardServiceAdapter());
95 }
96
97 return mInstance;
98}
99
100int
101SmartCardServiceAdapter::compareVersions(
102 const std::string& providedVersion, const std::string& localVersion) const
103{
104 const std::regex re("[.]");
105
106 std::vector<std::string> providedVersions;
107 std::split(providedVersion, re, std::back_inserter(providedVersions));
108
109 std::vector<std::string> localVersions;
110 std::split(localVersion, re, std::back_inserter(localVersions));
111
112 if (providedVersions.size() != localVersions.size()) {
113 throw IllegalStateException(
114 "Inconsistent version numbers. Provided = " + providedVersion
115 + ", Local: " + localVersion);
116 }
117
118 int provided = 0;
119 int local = 0;
120
121 try {
122 for (const auto& v : providedVersions) {
123 provided += std::stoi(v);
124 provided *= 1000;
125 }
126 for (const auto& v : localVersions) {
127 local += std::stoi(v);
128 local *= 1000;
129 }
130
131 /* Replaced NumberFormatException by std::invalid_argument */
132 } catch (const std::invalid_argument&) {
133 throw IllegalStateException(
134 std::string("Bad version numbers: provided = ") + providedVersion
135 + ", local = " + localVersion);
136 }
137
138 /* Java uses compareTo() which returns 0 when equal */
139 return provided != local;
140}
141
142void
143SmartCardServiceAdapter::checkPluginVersion(
144 const std::shared_ptr<PluginFactorySpi> pluginFactorySpi)
145{
146 if (compareVersions(
147 pluginFactorySpi->getCommonApiVersion(),
148 CommonApiProperties_VERSION)
149 != 0) {
150 mLogger->warn(
151 MSG_VERSION_MISMATCH_DETECTED,
152 std::string("plugin"),
153 pluginFactorySpi->getPluginName(),
154 std::string("Common API"),
155 pluginFactorySpi->getCommonApiVersion(),
156 CommonApiProperties_VERSION);
157 }
158
159 if (compareVersions(
160 pluginFactorySpi->getPluginApiVersion(),
161 PluginApiProperties_VERSION)
162 != 0) {
163 mLogger->warn(
164 MSG_VERSION_MISMATCH_DETECTED,
165 std::string("plugin"),
166 pluginFactorySpi->getPluginName(),
167 std::string("Plugin API"),
168 pluginFactorySpi->getPluginApiVersion(),
169 PluginApiProperties_VERSION);
170 }
171}
172
173std::shared_ptr<Plugin>
174SmartCardServiceAdapter::registerPlugin(
175 const std::shared_ptr<KeyplePluginExtensionFactory> pluginFactory)
176{
177 Assert::getInstance().notNull(pluginFactory, "pluginFactory");
178
179 std::shared_ptr<AbstractPluginAdapter> plugin = nullptr;
180
181 const std::lock_guard<std::mutex> lock(mMutex);
182
183 try {
184 const auto factorySpi
185 = std::dynamic_pointer_cast<PluginFactorySpi>(pluginFactory);
186 const auto poolSpi
187 = std::dynamic_pointer_cast<PoolPluginFactorySpi>(pluginFactory);
188 if (factorySpi) {
189 plugin = createLocalPlugin(factorySpi);
190 } else if (poolSpi) {
191 plugin = createLocalPoolPlugin(poolSpi);
192 // } else if (pluginFactory instanceof RemotePluginFactorySpi) {
193 // plugin = createRemotePlugin((RemotePluginFactorySpi)
194 // pluginFactory);
195 } else {
196 throw IllegalArgumentException(
197 std::string(
198 "Cannot cast the provided factory to PluginFactorySpi, "
199 "PoolPluginFactorySpi or RemotePluginFactorySpi. Actual "
200 "type: ")
201 + typeid(pluginFactory).name());
202 }
203
204 mPlugins.insert({plugin->getName(), plugin});
205 plugin->doRegister();
206
207 } catch (const IllegalArgumentException& e) {
208 throw IllegalArgumentException(
209 "The provided plugin factory doesn't implement the plugin"
210 " API properly",
211 e);
212
213 } catch (const PluginIOException& e) {
214 throw KeyplePluginException(
215 std::string("Failed to register plugin: ") + plugin->getName(), e);
216 }
217
218 return plugin;
219}
220
221void
222SmartCardServiceAdapter::unregisterPlugin(const std::string& pluginName)
223{
224 mLogger->info("Unregistering plugin [plugin=%]\n", pluginName);
225
226 const std::lock_guard<std::mutex> lock(mMutex);
227
228 auto i = mPlugins.find(pluginName);
229 if (i != mPlugins.end()) {
230 std::shared_ptr<Plugin> removedPlugin = std::move(i->second);
231 std::dynamic_pointer_cast<AbstractPluginAdapter>(removedPlugin)
232 ->doUnregister();
233 mPlugins.erase(i);
234
235 } else {
236 mLogger->warn("Plugin not registered [plugin=%]\n", pluginName);
237 }
238}
239
240const std::vector<std::string>
241SmartCardServiceAdapter::getPluginNames() const
242{
243 std::vector<std::string> pluginNames;
244 for (const auto& pair : mPlugins) {
245 pluginNames.push_back(pair.first);
246 }
247
248 return pluginNames;
249}
250
251const std::vector<std::shared_ptr<Plugin>>
252SmartCardServiceAdapter::getPlugins() const
253{
254 std::vector<std::shared_ptr<Plugin>> plugins;
255 for (const auto& pair : mPlugins) {
256 plugins.push_back(pair.second);
257 }
258
259 return plugins;
260}
261
262std::shared_ptr<Plugin>
263SmartCardServiceAdapter::getPlugin(const std::string& pluginName) const
264{
265 auto it = mPlugins.find(pluginName);
266 if (it != mPlugins.end()) {
267 return it->second;
268 }
269
270 return nullptr;
271}
272
273std::shared_ptr<Plugin>
274SmartCardServiceAdapter::getPlugin(
275 const std::shared_ptr<CardReader> cardReader) const
276{
277 for (const auto& plugin : mPlugins) {
278 const auto& readers = plugin.second->getReaders();
279 if (std::any_of(
280 readers.begin(),
281 readers.end(),
282 [cardReader](const std::shared_ptr<CardReader>& reader) {
283 return reader == cardReader;
284 })) {
285 return plugin.second;
286 }
287 }
288
289 return nullptr;
290}
291
292std::shared_ptr<CardReader>
293SmartCardServiceAdapter::getReader(const std::string& readerName) const
294{
295 for (const auto& plugin : mPlugins) {
296 const auto& readers = plugin.second->getReaders();
297 auto it = std::find_if(
298 readers.begin(),
299 readers.end(),
300 [readerName](const std::shared_ptr<CardReader>& reader) {
301 return reader->getName() == readerName;
302 });
303 if (it != readers.end()) {
304 return *it;
305 }
306 }
307
308 return nullptr;
309}
310
311std::shared_ptr<CardReader>
312SmartCardServiceAdapter::findReader(const std::string& readerNameRegex) const
313{
314 for (const auto& plugin : mPlugins) {
315 std::shared_ptr<CardReader> reader(
316 plugin.second->findReader(readerNameRegex));
317 if (reader != nullptr) {
318 return reader;
319 }
320 }
321
322 return nullptr;
323}
324
325void
326SmartCardServiceAdapter::checkCardExtension(
327 const std::shared_ptr<KeypleCardExtension> cardExtension) const
328{
329 checkCardExtensionVersion(cardExtension);
330}
331
332std::unique_ptr<ReaderApiFactory>
333SmartCardServiceAdapter::getReaderApiFactory()
334{
335 return std::unique_ptr<ReaderApiFactoryAdapter>(
336 new ReaderApiFactoryAdapter());
337}
338
339void
340SmartCardServiceAdapter::checkPoolPluginVersion(
341 const std::shared_ptr<PoolPluginFactorySpi> poolPluginFactorySpi)
342{
343 if (compareVersions(
344 poolPluginFactorySpi->getCommonApiVersion(),
345 CommonApiProperties_VERSION)
346 != 0) {
347 mLogger->warn(
348 MSG_VERSION_MISMATCH_DETECTED,
349 std::string("pool plugin"),
350 poolPluginFactorySpi->getPoolPluginName(),
351 std::string("Common API"),
352 poolPluginFactorySpi->getCommonApiVersion(),
353 CommonApiProperties_VERSION);
354 }
355
356 if (compareVersions(
357 poolPluginFactorySpi->getPluginApiVersion(),
358 PluginApiProperties_VERSION)
359 != 0) {
360 mLogger->warn(
361 MSG_VERSION_MISMATCH_DETECTED,
362 std::string("pool plugin"),
363 poolPluginFactorySpi->getPoolPluginName(),
364 std::string("Plugin API"),
365 poolPluginFactorySpi->getPluginApiVersion(),
366 PluginApiProperties_VERSION);
367 }
368}
369
370void
371SmartCardServiceAdapter::checkCardExtensionVersion(
372 const std::shared_ptr<KeypleCardExtension> cardExtension) const
373{
374 if (compareVersions(
375 cardExtension->getCommonApiVersion(), CommonApiProperties_VERSION)
376 != 0) {
377 const auto& c = *cardExtension.get();
378 mLogger->warn(
379 MSG_VERSION_MISMATCH_DETECTED,
380 std::string("card extension"),
381 std::string(typeid(c).name()),
382 std::string("Common API"),
383 cardExtension->getCommonApiVersion(),
384 CommonApiProperties_VERSION);
385 }
386
387 if (compareVersions(
388 cardExtension->getCardApiVersion(), CardApiProperties_VERSION)
389 != 0) {
390 const auto& c = *cardExtension.get();
391 mLogger->warn(
392 MSG_VERSION_MISMATCH_DETECTED,
393 std::string("card extension"),
394 std::string(typeid(c).name()),
395 std::string("Card API"),
396 cardExtension->getCardApiVersion(),
397 CardApiProperties_VERSION);
398 }
399
400 if (compareVersions(
401 cardExtension->getReaderApiVersion(), ReaderApiProperties_VERSION)
402 != 0) {
403 const auto& c = *cardExtension.get();
404 mLogger->warn(
405 MSG_VERSION_MISMATCH_DETECTED,
406 std::string("card extension"),
407 std::string(typeid(c).name()),
408 std::string("Reader API"),
409 cardExtension->getReaderApiVersion(),
410 ReaderApiProperties_VERSION);
411 }
412}
413
414void
415SmartCardServiceAdapter::checkPluginRegistration(const std::string& pluginName)
416{
417 mLogger->info("Registering plugin [plugin=%]\n", pluginName);
418
419 const auto it = mPlugins.find(pluginName);
420 if (it != mPlugins.end()) {
421 throw IllegalStateException(
422 "Plugin '" + pluginName
423 + "' has already been registered to the service");
424 }
425}
426
427std::shared_ptr<AbstractPluginAdapter>
428SmartCardServiceAdapter::createLocalPlugin(
429 std::shared_ptr<PluginFactorySpi> pluginFactorySpi)
430{
431 checkPluginRegistration(pluginFactorySpi->getPluginName());
432 checkPluginVersion(pluginFactorySpi);
433
434 std::shared_ptr<PluginSpi> pluginSpi = pluginFactorySpi->getPlugin();
435
436 if (pluginSpi->getName() != pluginFactorySpi->getPluginName()) {
437 throw IllegalArgumentException(
438 std::string("Plugin name '") + pluginSpi->getName()
439 + "' mismatches the expected name '"
440 + pluginFactorySpi->getPluginName() + "' provided by the factory");
441 }
442
443 std::shared_ptr<AbstractPluginAdapter> plugin = nullptr;
444
445 auto observable = std::dynamic_pointer_cast<ObservablePluginSpi>(pluginSpi);
446 if (observable) {
447 plugin = std::make_shared<ObservableLocalPluginAdapter>(observable);
448 } else {
449 auto autonomous
450 = std::dynamic_pointer_cast<AutonomousObservablePluginSpi>(
451 pluginSpi);
452 if (autonomous) {
453 plugin = std::make_shared<AutonomousObservableLocalPluginAdapter>(
454 autonomous);
455 } else {
456 plugin = std::make_shared<LocalPluginAdapter>(pluginSpi);
457 }
458 }
459
460 return plugin;
461}
462
463std::shared_ptr<AbstractPluginAdapter>
464SmartCardServiceAdapter::createLocalPoolPlugin(
465 std::shared_ptr<PoolPluginFactorySpi> poolPluginFactorySpi)
466{
467 checkPluginRegistration(poolPluginFactorySpi->getPoolPluginName());
468 checkPoolPluginVersion(poolPluginFactorySpi);
469
470 std::shared_ptr<PoolPluginSpi> poolPluginSpi
471 = poolPluginFactorySpi->getPoolPlugin();
472
473 if (poolPluginSpi->getName() != poolPluginFactorySpi->getPoolPluginName()) {
474 throw IllegalArgumentException(
475 std::string("Pool plugin name '") + poolPluginSpi->getName()
476 + "' mismatches the expected name '"
477 + poolPluginFactorySpi->getPoolPluginName()
478 + "' provided by the factory");
479 }
480
481 return std::make_shared<LocalPoolPluginAdapter>(poolPluginSpi);
482}
483
484} /* namespace service */
485} /* namespace core */
486} /* namespace keyple */