Keyple Service Resource C++ Library - 3.1.1
Component of the Keyple C++ middleware
CardProfileManagerAdapter.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/CardProfileManagerAdapter.hpp"
15
16#include <algorithm>
17#include <random>
18
19#include "keyple/core/service/Plugin.hpp"
20#include "keyple/core/service/SmartCardServiceProvider.hpp"
21#include "keyple/core/service/resource/CardResourceServiceAdapter.hpp"
22#include "keyple/core/util/cpp/Arrays.hpp"
23#include "keyple/core/util/cpp/System.hpp"
24#include "keyple/core/util/cpp/Thread.hpp"
25#include "keyple/core/util/cpp/exception/IllegalStateException.hpp"
26#include "keyple/core/util/cpp/exception/InterruptedException.hpp"
27
28namespace keyple {
29namespace core {
30namespace service {
31namespace resource {
32
33using keyple::core::service::Plugin;
34using keyple::core::service::SmartCardServiceProvider;
35using keyple::core::service::resource::CardResourceServiceAdapter;
36using keyple::core::util::cpp::Arrays;
37using keyple::core::util::cpp::System;
38using keyple::core::util::cpp::Thread;
39using keyple::core::util::cpp::exception::IllegalStateException;
40using keyple::core::util::cpp::exception::InterruptedException;
41
43
44CardProfileManagerAdapter::CardProfileManagerAdapter(
45 std::shared_ptr<CardResourceProfileConfigurator> cardProfile,
46 std::shared_ptr<CardResourceServiceConfiguratorAdapter> globalConfiguration)
47: mCardProfile(cardProfile)
48, mGlobalConfiguration(globalConfiguration)
49, mService(CardResourceServiceAdapter::getInstance())
50{
51 /* Prepare filter on reader name if requested */
52 if (cardProfile->getReaderNameRegex() != "") {
53 mReaderNameRegexPattern
54 = Pattern::compile(cardProfile->getReaderNameRegex());
55 } else {
56 mReaderNameRegexPattern = nullptr;
57 }
58
59 /* Initialize all available card resources */
60 if (cardProfile->getPlugins().empty()) {
61 initializeCardResourcesUsingDefaultPlugins();
62 } else {
63 initializeCardResourcesUsingProfilePlugins();
64 }
65}
66
67void
68CardProfileManagerAdapter::removeCardResource(
69 std::shared_ptr<CardResource> cardResource)
70{
71 const auto it
72 = std::find(mCardResources.begin(), mCardResources.end(), cardResource);
73
74 if (it != mCardResources.end()) {
75 mCardResources.erase(it);
76 }
77}
78
79void
80CardProfileManagerAdapter::onReaderConnected(
81 std::shared_ptr<ReaderManagerAdapter> readerManager)
82{
83 if (mCardProfile->getPlugins().empty()) {
84 initializeCardResource(readerManager);
85 } else {
86 const auto& profilePlugins = mCardProfile->getPlugins();
87 const auto it = std::find_if(
88 profilePlugins.begin(),
89 profilePlugins.end(),
90 [readerManager](const std::shared_ptr<Plugin>& profilePlugin) {
91 return (profilePlugin == readerManager->getPlugin());
92 });
93
94 if (it != profilePlugins.end()) {
95 initializeCardResource(readerManager);
96 return;
97 }
98 }
99}
100
101void
102CardProfileManagerAdapter::onCardInserted(
103 std::shared_ptr<ReaderManagerAdapter> readerManager)
104{
105 onReaderConnected(readerManager);
106}
107
108std::shared_ptr<CardResource>
109CardProfileManagerAdapter::getCardResource()
110{
111 std::shared_ptr<CardResource> cardResource = nullptr;
112 uint64_t maxTime = System::currentTimeMillis()
113 + mGlobalConfiguration->getTimeoutMillis();
114
115 do {
116 if (mPlugins.empty()) {
117 cardResource = getPoolCardResource();
118 } else {
119 if (mPoolPlugins.empty()) {
120 cardResource = getRegularCardResource();
121 } else {
122 cardResource = getRegularOrPoolCardResource();
123 }
124 }
125
126 pauseIfNeeded(cardResource);
127
128 } while (cardResource == nullptr
129 && mGlobalConfiguration->isBlockingAllocationMode()
130 && System::currentTimeMillis() <= maxTime);
131
132 return cardResource;
133}
134
135void
136CardProfileManagerAdapter::initializeCardResourcesUsingProfilePlugins()
137{
138 for (const auto& plugin : mCardProfile->getPlugins()) {
139 const auto poolPlugin = std::dynamic_pointer_cast<PoolPlugin>(plugin);
140 if (poolPlugin) {
141 mPoolPlugins.push_back(poolPlugin);
142 } else {
143 mPlugins.push_back(plugin);
144 initializeCardResources(plugin);
145 }
146 }
147}
148
149void
150CardProfileManagerAdapter::initializeCardResourcesUsingDefaultPlugins()
151{
152 const auto& poolPlugins = mGlobalConfiguration->getPoolPlugins();
153 mPoolPlugins.insert(
154 std::end(mPoolPlugins), std::begin(poolPlugins), std::end(poolPlugins));
155
156 for (const auto& plugin : mGlobalConfiguration->getPlugins()) {
157 mPlugins.push_back(plugin);
158 initializeCardResources(plugin);
159 }
160}
161
162void
163CardProfileManagerAdapter::initializeCardResources(
164 std::shared_ptr<Plugin> plugin)
165{
166 for (const auto& reader : plugin->getReaders()) {
167 std::shared_ptr<ReaderManagerAdapter> readerManager
168 = mService->getReaderManager(reader);
169 initializeCardResource(readerManager);
170 }
171}
172
173void
174CardProfileManagerAdapter::initializeCardResource(
175 std::shared_ptr<ReaderManagerAdapter> readerManager)
176{
177 if (isReaderAccepted(readerManager->getReader())) {
178 readerManager->activate();
179
180 std::shared_ptr<CardResourceAdapter> cardResource
181 = readerManager->matches(
182 mCardProfile->getCardResourceProfileExtension());
183
184 /*
185 * The returned card resource may already be present in the current list
186 * if the service starts with an observable reader in which a card has
187 * been inserted.
188 */
189 if (cardResource != nullptr) {
190 if (!Arrays::contains(mCardResources, cardResource)) {
191 mCardResources.push_back(cardResource);
192 mLogger->info(
193 "New card resource added [profile=%, cardResource=%]\n",
194 mCardProfile->getProfileName(),
195 CardResourceServiceAdapter::getCardResourceInfo(
196 cardResource));
197 } else {
198 mLogger->info(
199 "Card resource already present [profile=%, cardResource=%]\n",
200 mCardProfile->getProfileName(),
201 CardResourceServiceAdapter::getCardResourceInfo(
202 cardResource));
203 }
204 }
205 }
206}
207
208bool
209CardProfileManagerAdapter::isReaderAccepted(std::shared_ptr<CardReader> reader)
210{
211 return mReaderNameRegexPattern == nullptr
212 || mReaderNameRegexPattern->matcher(reader->getName())->matches();
213}
214
215void
216CardProfileManagerAdapter::pauseIfNeeded(
217 std::shared_ptr<CardResource> cardResource)
218{
219 if (cardResource == nullptr
220 && mGlobalConfiguration->isBlockingAllocationMode()) {
221 try {
222 Thread::sleep(mGlobalConfiguration->getCycleDurationMillis());
223 } catch (const InterruptedException& e) {
224 mLogger->error("Unexpected sleep interruption", e);
225 // FIXME: Thread::currentThread().interrupt();
226 }
227 }
228}
229
230std::shared_ptr<CardResource>
231CardProfileManagerAdapter::getRegularOrPoolCardResource()
232{
233 std::shared_ptr<CardResource> cardResource = nullptr;
234
235 if (mGlobalConfiguration->isUsePoolFirst()) {
236 cardResource = getPoolCardResource();
237 if (cardResource == nullptr) {
238 cardResource = getRegularCardResource();
239 }
240 } else {
241 cardResource = getRegularCardResource();
242 if (cardResource == nullptr) {
243 cardResource = getPoolCardResource();
244 }
245 }
246
247 return cardResource;
248}
249
250std::shared_ptr<CardResource>
251CardProfileManagerAdapter::getRegularCardResource()
252{
253 std::shared_ptr<CardResource> result = nullptr;
254 std::vector<std::shared_ptr<CardResource>> unusableCardResources;
255
256 for (const auto& cardResource : mCardResources) {
257 std::shared_ptr<CardReader> reader = cardResource->getReader();
258 // FIXME synchronized (reader) {
259 std::shared_ptr<ReaderManagerAdapter> readerManager
260 = mService->getReaderManager(reader);
261 if (readerManager != nullptr) {
262 try {
263 if (readerManager->lock(
264 cardResource,
265 mCardProfile->getCardResourceProfileExtension())) {
266 int cardResourceIndex
267 = Arrays::indexOf(mCardResources, cardResource);
268 updateCardResourcesOrder(cardResourceIndex);
269 result = cardResource;
270 break;
271 }
272 } catch (const IllegalStateException& e) {
273 (void)e;
274 unusableCardResources.push_back(cardResource);
275 }
276 } else {
277 unusableCardResources.push_back(cardResource);
278 }
279 //}
280 }
281
282 /* Remove unusable card resources identified */
283 for (const auto& cardResource : unusableCardResources) {
284 mService->removeCardResource(cardResource);
285 }
286
287 return result;
288}
289
290void
291CardProfileManagerAdapter::updateCardResourcesOrder(const int cardResourceIndex)
292{
293 if (mGlobalConfiguration->getAllocationStrategy()
294 == AllocationStrategy::CYCLIC) {
295 std::rotate(
296 mCardResources.begin(),
297 mCardResources.begin() - cardResourceIndex - 1,
298 mCardResources.end());
299
300 } else if (
301 mGlobalConfiguration->getAllocationStrategy()
302 == AllocationStrategy::RANDOM) {
303 auto rng = std::default_random_engine {};
304 std::shuffle(std::begin(mCardResources), std::end(mCardResources), rng);
305 }
306}
307
308std::shared_ptr<CardResource>
309CardProfileManagerAdapter::getPoolCardResource()
310{
311 std::shared_ptr<CardResourceProfileExtension> cardProfileExtension
312 = mCardProfile->getCardResourceProfileExtension();
313
314 for (const std::shared_ptr<PoolPlugin>& poolPlugin : mPoolPlugins) {
315 try {
316 std::shared_ptr<CardReader> reader = poolPlugin->allocateReader(
317 mCardProfile->getReaderGroupReference());
318
319 if (reader != nullptr) {
320 std::shared_ptr<SmartCard> selectedSmartCard
321 = poolPlugin->getSelectedSmartCard(reader);
322
323 std::shared_ptr<SmartCard> smartCard
324 = selectedSmartCard != nullptr
325 ? cardProfileExtension->matches(selectedSmartCard)
326 : cardProfileExtension->matches(
327 reader,
328 SmartCardServiceProvider::getService()
329 ->getReaderApiFactory());
330
331 if (smartCard != nullptr) {
332 std::shared_ptr<KeypleReaderExtension> readerExtension
333 = poolPlugin->getReaderExtension(
334 typeid(KeypleReaderExtension), reader->getName());
335
336 auto cardResource = std::make_shared<CardResourceAdapter>(
337 reader, readerExtension, smartCard);
338
339 mService->registerPoolCardResource(
340 cardResource, poolPlugin);
341
342 return cardResource;
343 }
344 } else {
345 releaseReaderSilently(poolPlugin, reader);
346 }
347
348 } catch (const KeyplePluginException&) {
349 /* Continue */
350 }
351 }
352
353 return nullptr;
354}
355
356void
357CardProfileManagerAdapter::releaseReaderSilently(
358 std::shared_ptr<PoolPlugin> poolPlugin, std::shared_ptr<CardReader> reader)
359{
360 try {
361 poolPlugin->releaseReader(reader);
362 } catch (const Exception&) {
363 /* NOP £*/
364 }
365}
366
367} /* namespace resource */
368} /* namespace service */
369} /* namespace core */
370} /* namespace keyple */
PluginsConfigurator::AllocationStrategy AllocationStrategy