diff --git a/.github/scripts/prepare_doxygen.sh b/.github/scripts/prepare_doxygen.sh
deleted file mode 100644
index f87b122..0000000
--- a/.github/scripts/prepare_doxygen.sh
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/sh
-
-project_version=$1
-
-project_name="$(head -n 1 README.md | sed 's/#//')"
-
-repository_name=$(git rev-parse --show-toplevel | xargs basename)
-
-echo "Clone $repository_name..."
-git clone --branch doc https://github.com/eclipse/"$repository_name".git
-
-cd "$repository_name" || exit
-
-echo "Delete existing SNAPSHOT directory..."
-rm -rf ./*-SNAPSHOT
-
-echo "Create target directory $project_version..."
-mkdir "$project_version"
-
-echo "Copy Doxygen doc..."
-cp -rf ../.github/doxygen/out/html/* "$project_version"/
-
-echo "Update versions list..."
-echo "| Version | Documents |" >list_versions.md
-echo "|:---:|---|" >>list_versions.md
-for directory in $(ls -rvd [0-9]*/ | sort -t. -k 1,1nr -k 2,2nr -k 3,3nr -k 4,4nr | cut -f1 -d'/'); do
- echo "| $directory | [API documentation]($directory) |" >>list_versions.md
-done
-
-echo "Computed all versions:"
-cat list_versions.md
-
-echo "Update project name to $project_name"
-sed -i "2s/.*/title: $project_name/" _config.yml
-
-cd ..
-
-echo "Local docs update finished."
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index 248a504..8778107 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -1,4 +1,4 @@
-name: Build and test code
+name: Build and Test code
on:
push:
@@ -6,45 +6,9 @@ on:
- main
pull_request:
-jobs:
- build:
-
- strategy:
- matrix:
- env:
- - toolchain: "toolchain/gcc-linux.cmake"
- runner: ubuntu-latest
- generator: ""
- - toolchain: "toolchain/clang-macos.cmake"
- runner: macos-latest
- generator: ""
- - toolchain: "\"toolchain/clang-windows.cmake\""
- runner: windows-latest
- generator: "-G \"Visual Studio 17 2022\""
-
- runs-on: ${{ matrix.env.runner }}
-
- steps:
- - name: Check out repository
- uses: actions/checkout@v4
- - name: Install Linux reqs
- if: ${{ matrix.env.runner == 'ubuntu-latest' }}
- run: |
- sudo apt-get update
- sudo apt-get install -y clang cmake cppcheck clang-format clang-tidy gcc pre-commit
-
- - name: Install macOS reqs
- if: ${{ matrix.env.runner == 'macos-latest' }}
- run: |
- brew install llvm cppcheck clang-format gcc pre-commit
-
- - name: Build
- run: |
- cmake ${{ matrix.env.generator }} -B build -S . -DCMAKE_TOOLCHAIN_FILE=${{ matrix.env.toolchain }}
- cmake --build build -j8
-
- - name: Run Linux/macOS tests
- if: ${{ matrix.env.runner == 'ubuntu-latest' || matrix.env.runner == 'macos-latest' }}
- run: |
- ./build/bin/keypleservicecpplib_ut
+jobs:
+ call-reusable-workflow:
+ uses: eclipse-keyple/keyple-actions/.github/workflows/reusable-cpp-build-and-test.yml@main # NOSONAR - Same organization, trusted source
+ with:
+ test_executable_name: 'build/bin/keypleservicecpplib_ut'
diff --git a/.github/workflows/publish-doc-release.yml b/.github/workflows/publish-doc-release.yml
index 8e558fe..d26dd8d 100644
--- a/.github/workflows/publish-doc-release.yml
+++ b/.github/workflows/publish-doc-release.yml
@@ -1,47 +1,14 @@
-name: Publish API documentation
-
+name: Publish API documentation (release)
on:
release:
types: [published]
+permissions:
+ checks: write
+
jobs:
publish-doc-release:
- runs-on: ubuntu-latest
- steps:
- - name: Check out repository code from ${{ github.repository }}/${{ github.ref }}
- uses: actions/checkout@v3
- with:
- fetch-depth: 0
-
- - name: Make scripts executable
- run: chmod +x ./.github/scripts/*.sh
-
- - name: Check version
- working-directory: .
- run: ./.github/scripts/check_version.sh $(echo "${{ github.ref }}" | sed -e "s,^refs/tags/,,")
-
- - name: Patch Doxyfile
- working-directory: .
- run: ./.github/scripts/patch_doxyfile.sh ${{ env.PROJECT_VERSION }}
-
- - name: Generate project documentation with Doxygen
- uses: eclipse-keyple/keyple-actions/doxygen@0c8593c4ed6b393642a1732af1fdef84d5e59540
- with:
- working-directory: .github/doxygen/
- doxyfile-path: ./Doxyfile
-
- - name: Prepare Doxygen doc page locally
- working-directory: .
- run: ./.github/scripts/prepare_doxygen.sh ${{ env.PROJECT_VERSION }}
-
- - name: Deploy to doc branch
- run: |
- git config --global user.name "Eclipse Keypop Bot"
- git config --global user.email "${{ github.repository }}-bot@eclipse.org"
- cd ${{ github.event.repository.name }}
- git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
- git add .
- git commit -m "docs: update ${{ github.event.inputs.version || github.ref_name }} documentation"
- git push origin doc --force
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ uses: eclipse-keyple/keyple-actions/.github/workflows/reusable-publish-doxygen.yml@main # NOSONAR - Same organization, trusted source
+ with:
+ version: ${{ github.event.inputs.version || github.ref_name }}
+ secrets: inherit # NOSONAR - Same organization, trusted source
diff --git a/.github/workflows/publish-doc-snapshot.yml b/.github/workflows/publish-doc-snapshot.yml
index 7094036..d5d29a6 100644
--- a/.github/workflows/publish-doc-snapshot.yml
+++ b/.github/workflows/publish-doc-snapshot.yml
@@ -1,50 +1,14 @@
name: Publish API documentation (snapshot)
-
on:
push:
branches:
- main
- - master
- - doxygen
+ workflow_dispatch:
+
+permissions:
+ checks: write
jobs:
publish-doc-snapshot:
- runs-on: ubuntu-latest
- steps:
- - name: Check out repository code from ${{ github.repository }}/${{ github.ref }}
- uses: actions/checkout@v3
- with:
- fetch-depth: 0
-
- - name: Make scripts executable
- run: chmod +x ./.github/scripts/*.sh
-
- - name: Check version
- working-directory: .
- run: ./.github/scripts/check_version.sh
-
- - name: Patch Doxyfile
- working-directory: .
- run: ./.github/scripts/patch_doxyfile.sh ${{ env.PROJECT_VERSION }}
-
- - name: Generate project documentation with Doxygen
- uses: eclipse-keyple/keyple-actions/doxygen@0c8593c4ed6b393642a1732af1fdef84d5e59540
- with:
- working-directory: .github/doxygen/
- doxyfile-path: ./Doxyfile
-
- - name: Prepare Doxygen doc page locally
- working-directory: .
- run: ./.github/scripts/prepare_doxygen.sh ${{ env.PROJECT_VERSION }}
-
- - name: Deploy to doc branch
- run: |
- git config --global user.name "Eclipse Keypop Bot"
- git config --global user.email "${{ github.repository }}-bot@eclipse.org"
- cd ${{ github.event.repository.name }}
- git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
- git add .
- git diff --quiet && git diff --staged --quiet || git commit -m "docs: update snapshot documentation"
- git push origin doc --force
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ uses: eclipse-keyple/keyple-actions/.github/workflows/reusable-publish-doxygen.yml@main # NOSONAR - Same organization, trusted source
+ secrets: inherit # NOSONAR - Same organization, trusted source
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a99e38c..4cbc283 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,12 +13,12 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
PROJECT(KeypleServiceCppLib
- VERSION 3.2.3
+ VERSION 3.3.5
LANGUAGES C CXX)
SET(CMAKE_PROJECT_VERSION_MAJOR "3")
-SET(CMAKE_PROJECT_VERSION_MINOR "2")
-SET(CMAKE_PROJECT_VERSION_PATCH "3")
+SET(CMAKE_PROJECT_VERSION_MINOR "3")
+SET(CMAKE_PROJECT_VERSION_PATCH "5")
SET(CMAKE_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_MAJOR}.
${CMAKE_PROJECT_VERSION_MINOR}.
diff --git a/include/keyple/core/service/AbstractReaderAdapter.hpp b/include/keyple/core/service/AbstractReaderAdapter.hpp
index 08dd728..ac62b26 100644
--- a/include/keyple/core/service/AbstractReaderAdapter.hpp
+++ b/include/keyple/core/service/AbstractReaderAdapter.hpp
@@ -81,6 +81,22 @@ class KEYPLESERVICE_API AbstractReaderAdapter : virtual public CardReader,
*/
const std::string& getPluginName() const;
+ /**
+ * Returns the {@link KeypleReaderExtension} that is reader-specific.
+ *
+ *
Note: the provided argument is used at compile time to check the type
+ * consistency.
+ *
+ * @param readerExtensionClass The specific class of the reader.
+ * @param The type of the reader extension.
+ * @return A {@link KeypleReaderExtension}.
+ * @throw IllegalStateException If reader is no longer registered.
+ * @since 2.0.0
+ */
+ std::shared_ptr
+ getExtension(const std::type_info& readerExtensionClass) const;
+ // FIXME? should be final?
+
/**
* Performs a selection scenario following a card detection.
*
@@ -194,14 +210,6 @@ class KEYPLESERVICE_API AbstractReaderAdapter : virtual public CardReader,
*/
const std::string& getName() const final;
- /**
- * {@inheritDoc}
- *
- * @since 2.0.0
- */
- std::shared_ptr
- getExtension(const std::type_info& readerExtensionClass) const;
-
/**
* {@inheritDoc}
*
diff --git a/include/keyple/core/service/ApduResponseAdapter.hpp b/include/keyple/core/service/ApduResponseAdapter.hpp
index 75cd7c9..b727a2a 100644
--- a/include/keyple/core/service/ApduResponseAdapter.hpp
+++ b/include/keyple/core/service/ApduResponseAdapter.hpp
@@ -60,7 +60,14 @@ class KEYPLESERVICE_API ApduResponseAdapter final : public ApduResponseApi {
*
* @since 2.0.0
*/
- const std::vector getDataOut() const override;
+ void setApdu(const std::vector& apdu) override;
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 2.0.0
+ */
+ std::vector getDataOut() const override;
/**
* {@inheritDoc}
@@ -85,7 +92,7 @@ class KEYPLESERVICE_API ApduResponseAdapter final : public ApduResponseApi {
/**
*
*/
- const std::vector mApdu;
+ std::vector mApdu;
/**
*
diff --git a/include/keyple/core/service/InternalDto.hpp b/include/keyple/core/service/InternalDto.hpp
index cab0f78..6e04383 100644
--- a/include/keyple/core/service/InternalDto.hpp
+++ b/include/keyple/core/service/InternalDto.hpp
@@ -74,9 +74,9 @@ class KEYPLESERVICE_API InternalDto {
/**
*
*/
- const std::shared_ptr
- parse(const std::shared_ptr
- cardSelectionResponseApi) const override;
+ const std::shared_ptr parse(
+ const std::shared_ptr
+ cardSelectionResponseApi) const override;
private:
/**
diff --git a/include/keyple/core/service/InternalLegacyDto.hpp b/include/keyple/core/service/InternalLegacyDto.hpp
index 51b761c..6087916 100644
--- a/include/keyple/core/service/InternalLegacyDto.hpp
+++ b/include/keyple/core/service/InternalLegacyDto.hpp
@@ -80,6 +80,22 @@ class KEYPLESERVICE_API InternalLegacyDto final {
std::vector mSuccessfulSelectionStatusWords;
};
+ /**
+ * @since 3.3.1
+ */
+ class LegacyCardRequestV0 final {
+ public:
+ /**
+ *
+ */
+ std::vector> mApduRequests;
+
+ /**
+ *
+ */
+ bool mIsStatusCodesVerificationEnabled;
+ };
+
/**
* @since 2.1.1
*/
@@ -104,7 +120,7 @@ class KEYPLESERVICE_API InternalLegacyDto final {
/**
* @since 2.1.1
*/
- class LegacyCardRequest final {
+ class LegacyCardRequestV1 final {
public:
/**
*
@@ -117,10 +133,26 @@ class KEYPLESERVICE_API InternalLegacyDto final {
bool mStopOnUnsuccessfulStatusWord;
};
+ /**
+ * @since 3.3.1
+ */
+ class LegacyCardSelectionRequestV0 final {
+ public:
+ /**
+ *
+ */
+ std::shared_ptr mCardSelector;
+
+ /**
+ *
+ */
+ std::shared_ptr mCardRequest;
+ };
+
/**
* @since 2.1.1
*/
- class LegacyCardSelectionRequest final {
+ class LegacyCardSelectionRequestV1 final {
public:
/**
*
@@ -130,14 +162,23 @@ class KEYPLESERVICE_API InternalLegacyDto final {
/**
*
*/
- std::shared_ptr mCardRequest;
+ std::shared_ptr mCardRequest;
};
/**
*
*/
- static const std::vector>
- mapToLegacyCardSelectionRequests(
+ static const std::vector>
+ mapToLegacyCardSelectionRequestsV0(
+ const std::vector>& cardSelectors,
+ const std::vector>&
+ cardSelectionRequests);
+
+ /**
+ *
+ */
+ static const std::vector>
+ mapToLegacyCardSelectionRequestsV1(
const std::vector>& cardSelectors,
const std::vector>&
cardSelectionRequests);
@@ -145,8 +186,16 @@ class KEYPLESERVICE_API InternalLegacyDto final {
/**
*
*/
- static std::shared_ptr
- mapToLegacyCardSelectionRequest(
+ static std::shared_ptr
+ mapToLegacyCardSelectionRequestV0(
+ std::shared_ptr cardSelector,
+ std::shared_ptr cardSelectionRequestSpi);
+
+ /**
+ *
+ */
+ static std::shared_ptr
+ mapToLegacyCardSelectionRequestV1(
std::shared_ptr cardSelector,
std::shared_ptr cardSelectionRequestSpi);
@@ -161,7 +210,13 @@ class KEYPLESERVICE_API InternalLegacyDto final {
*
*/
static std::shared_ptr
- mapToLegacyCardRequest(const std::shared_ptr cardRequest);
+ mapToLegacyCardRequestV0(const std::shared_ptr cardRequest);
+
+ /**
+ *
+ */
+ static std::shared_ptr
+ mapToLegacyCardRequestV1(const std::shared_ptr cardRequest);
/**
*
diff --git a/include/keyple/core/service/LocalReaderAdapter.hpp b/include/keyple/core/service/LocalReaderAdapter.hpp
index c33104d..63dec5f 100644
--- a/include/keyple/core/service/LocalReaderAdapter.hpp
+++ b/include/keyple/core/service/LocalReaderAdapter.hpp
@@ -172,7 +172,6 @@ class KEYPLESERVICE_API LocalReaderAdapter : public AbstractReaderAdapter {
void releaseChannel() final;
/**
- * (private)
* Local implementation of ApduRequestSpi.
*/
class ApduRequest : public ApduRequestSpi {
@@ -187,7 +186,12 @@ class KEYPLESERVICE_API LocalReaderAdapter : public AbstractReaderAdapter {
/**
*
*/
- std::vector getApdu() const override;
+ const std::vector& getApdu() const override;
+
+ /**
+ * C++
+ */
+ void setApdu(const std::vector& apdu) override;
/**
*
@@ -442,8 +446,10 @@ class KEYPLESERVICE_API LocalReaderAdapter : public AbstractReaderAdapter {
* @param cardSelector A not null CardSelector.
* @param cardSelectionRequest A not null CardSelectionRequestSpi.
* @return A not null {@link SelectionStatus}.
- * @throw ReaderIOException if the communication with the reader has failed.
- * @throw CardIOException if the communication with the card has failed.
+ * @throw ReaderBrokenCommunicationException If the communication with the
+ * reader has failed.
+ * @throw CardBrokenCommunicationException If the communication with the
+ * card has failed.
*/
std::shared_ptr processSelection(
std::shared_ptr cardSelector,
@@ -453,6 +459,7 @@ class KEYPLESERVICE_API LocalReaderAdapter : public AbstractReaderAdapter {
* Attempts to select the card and executes the optional requests if any.
*
* @param cardSelectionRequest The CardSelectionRequestSpi to be processed.
+ * @param channelControl The channelControl.
* @return A not null reference.
* @throw ReaderBrokenCommunicationException If the communication with the
* reader has failed.
@@ -463,7 +470,8 @@ class KEYPLESERVICE_API LocalReaderAdapter : public AbstractReaderAdapter {
*/
std::shared_ptr processCardSelectionRequest(
std::shared_ptr cardSelector,
- std::shared_ptr cardSelectionRequest);
+ std::shared_ptr cardSelectionRequest,
+ const ChannelControl channelControl);
/**
* Transmits an ApduRequestSpi and receives the ApduResponseAdapter.
@@ -479,21 +487,6 @@ class KEYPLESERVICE_API LocalReaderAdapter : public AbstractReaderAdapter {
std::shared_ptr
processApduRequest(const std::shared_ptr apduRequest);
- /**
- * Transmits a CardRequestSpi and returns a CardResponseAdapter.
- *
- * @param cardRequest The card request to transmit.
- * @return A not null reference.
- * @throw ReaderBrokenCommunicationException If the communication with the
- * reader has failed.
- * @throw CardBrokenCommunicationException If the communication with the
- * card has failed.
- * @throw UnexpectedStatusWordException If status word verification is
- * enabled in the card request and the card returned an unexpected code.
- */
- std::shared_ptr
- processCardRequest(const std::shared_ptr cardRequest);
-
/**
*
*/
diff --git a/include/keyple/core/service/ObservableLocalReaderAdapter.hpp b/include/keyple/core/service/ObservableLocalReaderAdapter.hpp
index 883e6dd..defbe6b 100644
--- a/include/keyple/core/service/ObservableLocalReaderAdapter.hpp
+++ b/include/keyple/core/service/ObservableLocalReaderAdapter.hpp
@@ -469,6 +469,43 @@ class KEYPLESERVICE_API ObservableLocalReaderAdapter
cardSelectionResponses);
};
+/**
+ * Operator << for InternalEvent enum to enable readable logging.
+ *
+ * @param os The output stream.
+ * @param event The internal event.
+ * @return The output stream.
+ */
+inline std::ostream&
+operator<<(std::ostream& os,
+ const ObservableLocalReaderAdapter::InternalEvent event)
+{
+ switch (event) {
+ case ObservableLocalReaderAdapter::InternalEvent::CARD_INSERTED:
+ os << "CARD_INSERTED";
+ break;
+ case ObservableLocalReaderAdapter::InternalEvent::CARD_REMOVED:
+ os << "CARD_REMOVED";
+ break;
+ case ObservableLocalReaderAdapter::InternalEvent::CARD_PROCESSED:
+ os << "CARD_PROCESSED";
+ break;
+ case ObservableLocalReaderAdapter::InternalEvent::START_DETECT:
+ os << "START_DETECT";
+ break;
+ case ObservableLocalReaderAdapter::InternalEvent::STOP_DETECT:
+ os << "STOP_DETECT";
+ break;
+ case ObservableLocalReaderAdapter::InternalEvent::TIME_OUT:
+ os << "TIME_OUT";
+ break;
+ default:
+ os << "UNKNOWN_EVENT(" << static_cast(event) << ")";
+ break;
+ }
+ return os;
+}
+
} /* namespace service */
} /* namespace core */
} /* namespace keyple */
diff --git a/include/keyple/core/service/PluginEvent.hpp b/include/keyple/core/service/PluginEvent.hpp
index 6a2bcee..ae09794 100644
--- a/include/keyple/core/service/PluginEvent.hpp
+++ b/include/keyple/core/service/PluginEvent.hpp
@@ -93,20 +93,27 @@ class KEYPLESERVICE_API PluginEvent {
virtual Type getType() const = 0;
/**
+ * Operator << for PluginEvent::Type enum to enable readable logging.
*
+ * @param os The output stream.
+ * @param t The event type.
+ * @return The output stream.
*/
friend std::ostream&
operator<<(std::ostream& os, const Type t)
{
switch (t) {
case Type::READER_CONNECTED:
- os << "TYPE = READER_CONNECTED";
+ os << "READER_CONNECTED";
break;
case Type::READER_DISCONNECTED:
- os << "TYPE = READER_DISCONNECTED";
+ os << "READER_DISCONNECTED";
break;
case Type::UNAVAILABLE:
- os << "TYPE = UNAVAILABLE";
+ os << "UNAVAILABLE";
+ break;
+ default:
+ os << "UNKNOWN_TYPE(" << static_cast(t) << ")";
break;
}
diff --git a/include/keyple/core/service/SmartCardService.hpp b/include/keyple/core/service/SmartCardService.hpp
index bddfc79..f4afb9b 100644
--- a/include/keyple/core/service/SmartCardService.hpp
+++ b/include/keyple/core/service/SmartCardService.hpp
@@ -20,6 +20,7 @@
#include "keyple/core/common/KeypleCardExtension.hpp"
#include "keyple/core/common/KeyplePluginExtensionFactory.hpp"
#include "keyple/core/service/KeypleServiceExport.hpp"
+#include "keyple/core/service/Plugin.hpp"
#include "keypop/reader/ReaderApiFactory.hpp"
namespace keyple {
@@ -28,6 +29,7 @@ namespace service {
using keyple::core::common::KeypleCardExtension;
using keyple::core::common::KeyplePluginExtensionFactory;
+using keyple::core::service::Plugin;
using keypop::reader::ReaderApiFactory;
/**
@@ -134,7 +136,8 @@ class KEYPLESERVICE_API SmartCardService {
* @since 2.0.0
*/
virtual void checkCardExtension(
- const std::shared_ptr cardExtension) const = 0;
+ const std::shared_ptr cardExtension) const
+ = 0;
/**
* Registers a new distributed local service to the service.
diff --git a/include/keyple/core/service/cpp/ExecutorService.hpp b/include/keyple/core/service/cpp/ExecutorService.hpp
index eae19b1..6a4c068 100644
--- a/include/keyple/core/service/cpp/ExecutorService.hpp
+++ b/include/keyple/core/service/cpp/ExecutorService.hpp
@@ -13,7 +13,9 @@
#pragma once
+#include
#include
+#include
#include
#include
#include
@@ -74,25 +76,35 @@ class KEYPLESERVICE_API ExecutorService final {
/**
*
*/
- void run();
+ std::mutex mMutex;
+
+ /**
+ *
+ */
+ std::condition_variable mCondition;
/**
*
*/
- std::atomic mRunning;
+ bool mRunning;
/**
*
*/
- std::atomic mTerminated;
+ bool mTerminated;
/**
*
*/
- std::thread* mThread;
+ std::unique_ptr mThread;
+
+ /**
+ *
+ */
+ void run();
};
} /* namespace cpp */
} /* namespace service */
} /* namespace core */
-} /* namespace keyple */
+} /* namespace keyple */
\ No newline at end of file
diff --git a/src/main/AbstractReaderAdapter.cpp b/src/main/AbstractReaderAdapter.cpp
index ac15b62..2731545 100644
--- a/src/main/AbstractReaderAdapter.cpp
+++ b/src/main/AbstractReaderAdapter.cpp
@@ -53,6 +53,15 @@ AbstractReaderAdapter::getPluginName() const
return mPluginName;
}
+std::shared_ptr
+AbstractReaderAdapter::getExtension(
+ const std::type_info& /*readerExtensionClass*/) const
+{
+ checkStatus();
+
+ return mReaderExtension;
+}
+
const std::vector>
AbstractReaderAdapter::transmitCardSelectionRequests(
const std::vector>& cardSelectors,
@@ -130,17 +139,6 @@ AbstractReaderAdapter::getName() const
return mReaderName;
}
-std::shared_ptr
-AbstractReaderAdapter::getExtension(
- const std::type_info& readerExtensionClass) const
-{
- (void)readerExtensionClass;
-
- checkStatus();
-
- return mReaderExtension;
-}
-
const std::shared_ptr
AbstractReaderAdapter::transmitCardRequest(
const std::shared_ptr cardRequest,
diff --git a/src/main/ApduResponseAdapter.cpp b/src/main/ApduResponseAdapter.cpp
index 41d25b0..3199b75 100644
--- a/src/main/ApduResponseAdapter.cpp
+++ b/src/main/ApduResponseAdapter.cpp
@@ -39,7 +39,13 @@ ApduResponseAdapter::getApdu() const
return mApdu;
}
-const std::vector
+void
+ApduResponseAdapter::setApdu(const std::vector& apdu)
+{
+ mApdu = apdu;
+}
+
+std::vector
ApduResponseAdapter::getDataOut() const
{
return Arrays::copyOfRange(mApdu, 0, static_cast(mApdu.size()) - 2);
diff --git a/src/main/CardRemovalPassiveMonitoringJobAdapter.cpp b/src/main/CardRemovalPassiveMonitoringJobAdapter.cpp
index 32e6da5..e6bde18 100644
--- a/src/main/CardRemovalPassiveMonitoringJobAdapter.cpp
+++ b/src/main/CardRemovalPassiveMonitoringJobAdapter.cpp
@@ -59,6 +59,8 @@ void
CardRemovalPassiveMonitoringJobAdapter::CardRemovalPassiveMonitoringJob::
execute()
{
+ bool isTaskCanceled = false;
+
auto cardRemovalWaiterBlockingSpi
= std::dynamic_pointer_cast(
mParent->mReaderSpi);
@@ -92,6 +94,7 @@ CardRemovalPassiveMonitoringJobAdapter::CardRemovalPassiveMonitoringJob::
mParent->getReader()->getName(),
e.getMessage());
} catch (const TaskCanceledException& e) {
+ isTaskCanceled = true;
mParent->mLogger->warn(
"Monitoring job process cancelled: %\n", e.getMessage());
} catch (const RuntimeException& e) {
@@ -104,7 +107,9 @@ CardRemovalPassiveMonitoringJobAdapter::CardRemovalPassiveMonitoringJob::
}
/* Finally */
- mMonitoringState->onEvent(InternalEvent::CARD_REMOVED);
+ if (!isTaskCanceled) {
+ mMonitoringState->onEvent(InternalEvent::CARD_REMOVED);
+ }
}
/* CARD REMOVAL PASSIVE MONITORING JOB ADAPTER
diff --git a/src/main/InternalLegacyDto.cpp b/src/main/InternalLegacyDto.cpp
index e506823..9ceb673 100644
--- a/src/main/InternalLegacyDto.cpp
+++ b/src/main/InternalLegacyDto.cpp
@@ -26,32 +26,66 @@ InternalLegacyDto::InternalLegacyDto()
{
}
-const std::vector>
-InternalLegacyDto::mapToLegacyCardSelectionRequests(
+const std::vector>
+InternalLegacyDto::mapToLegacyCardSelectionRequestsV0(
const std::vector>& cardSelectors,
const std::vector>&
cardSelectionRequests)
{
- std::vector> result;
+ std::vector> result;
for (int i = 0; i < cardSelectors.size(); i++) {
- result.push_back(mapToLegacyCardSelectionRequest(
+ result.push_back(mapToLegacyCardSelectionRequestV0(
cardSelectors[i], cardSelectionRequests[i]));
}
return result;
}
-std::shared_ptr
-InternalLegacyDto::mapToLegacyCardSelectionRequest(
+const std::vector>
+InternalLegacyDto::mapToLegacyCardSelectionRequestsV1(
+ const std::vector>& cardSelectors,
+ const std::vector>&
+ cardSelectionRequests)
+{
+ std::vector> result;
+
+ for (int i = 0; i < cardSelectors.size(); i++) {
+ result.push_back(mapToLegacyCardSelectionRequestV1(
+ cardSelectors[i], cardSelectionRequests[i]));
+ }
+
+ return result;
+}
+
+std::shared_ptr
+InternalLegacyDto::mapToLegacyCardSelectionRequestV0(
+ std::shared_ptr cardSelector,
+ std::shared_ptr cardSelectionRequestSpi)
+{
+ auto result = std::make_shared();
+
+ result->mCardRequest = cardSelectionRequestSpi->getCardRequest() != nullptr
+ ? mapToLegacyCardRequestV0(
+ cardSelectionRequestSpi->getCardRequest())
+ : nullptr;
+
+ result->mCardSelector
+ = mapToLegacyCardSelector(cardSelector, cardSelectionRequestSpi);
+
+ return result;
+}
+
+std::shared_ptr
+InternalLegacyDto::mapToLegacyCardSelectionRequestV1(
std::shared_ptr cardSelector,
std::shared_ptr cardSelectionRequestSpi)
{
- auto result = std::make_shared();
+ auto result = std::make_shared();
result->mCardRequest = cardSelectionRequestSpi->getCardRequest() != nullptr
- ? mapToLegacyCardRequest(
- cardSelectionRequestSpi->getCardRequest())
+ ? mapToLegacyCardRequestV1(
+ cardSelectionRequestSpi->getCardRequest())
: nullptr;
result->mCardSelector
@@ -92,10 +126,24 @@ InternalLegacyDto::mapToLegacyCardSelector(
}
std::shared_ptr
-InternalLegacyDto::mapToLegacyCardRequest(
+InternalLegacyDto::mapToLegacyCardRequestV0(
+ const std::shared_ptr cardRequest)
+{
+ auto result = std::make_shared();
+
+ result->mApduRequests
+ = mapToLegacyApduRequests(cardRequest->getApduRequests());
+ result->mIsStatusCodesVerificationEnabled
+ = cardRequest->stopOnUnsuccessfulStatusWord();
+
+ return result;
+}
+
+std::shared_ptr
+InternalLegacyDto::mapToLegacyCardRequestV1(
const std::shared_ptr cardRequest)
{
- auto result = std::make_shared();
+ auto result = std::make_shared();
result->mApduRequests
= mapToLegacyApduRequests(cardRequest->getApduRequests());
diff --git a/src/main/LocalReaderAdapter.cpp b/src/main/LocalReaderAdapter.cpp
index 5fa32bc..6b9c96b 100644
--- a/src/main/LocalReaderAdapter.cpp
+++ b/src/main/LocalReaderAdapter.cpp
@@ -280,80 +280,71 @@ LocalReaderAdapter::processSelection(
std::shared_ptr cardSelector,
std::shared_ptr cardSelectionRequest)
{
- /* RL-CLA-CHAAUTO.1 */
- std::string powerOnData = "";
- std::shared_ptr fciResponse = nullptr;
- bool hasMatched = true;
-
- auto internalSelector
- = std::dynamic_pointer_cast(cardSelector);
- if (!internalSelector) {
- throw RuntimeException(
- "cardSelector is not of type InternalCardSelector.");
- }
+ try {
+ /* RL-CLA-CHAAUTO.1 */
+ std::string powerOnData = "";
+ std::shared_ptr fciResponse = nullptr;
+ bool hasMatched = true;
+
+ auto internalSelector
+ = std::dynamic_pointer_cast(cardSelector);
+ if (!internalSelector) {
+ throw RuntimeException(
+ "cardSelector is not of type InternalCardSelector.");
+ }
- const std::string& logicalProtocolName
- = internalSelector->getLogicalProtocolName();
- if (logicalProtocolName != "" && mUseDefaultProtocol) {
- throw IllegalStateException(
- "Protocol " + logicalProtocolName
- + " not associated to a reader protocol.");
- }
+ const std::string& logicalProtocolName
+ = internalSelector->getLogicalProtocolName();
+ if (logicalProtocolName != "" && mUseDefaultProtocol) {
+ throw IllegalStateException(
+ "Protocol " + logicalProtocolName
+ + " not associated to a reader protocol.");
+ }
- /* Check protocol if enabled */
- if (logicalProtocolName == ""
- || logicalProtocolName == mCurrentLogicalProtocolName) {
- /*
- * Protocol check succeeded, check power-on data if enabled
- * RL-ATR-FILTER
- * RL-SEL-USAGE.1
- */
- powerOnData = mReaderSpi->getPowerOnData();
- if (checkPowerOnData(powerOnData, internalSelector)) {
- /* No power-on data filter or power-on data check succeeded, select
- * by AID if enabled */
- const auto internalIsoCardSelector
- = std::dynamic_pointer_cast(
- cardSelector);
- if (internalIsoCardSelector
- && internalIsoCardSelector->getAid().size() != 0) {
- fciResponse = selectByAid(internalIsoCardSelector);
- const std::vector& statusWords
- = cardSelectionRequest->getSuccessfulSelectionStatusWords();
- hasMatched = std::find(
- statusWords.begin(),
- statusWords.end(),
- fciResponse->getStatusWord())
- != statusWords.end();
+ /* Check protocol if enabled */
+ if (logicalProtocolName == ""
+ || logicalProtocolName == mCurrentLogicalProtocolName) {
+ /*
+ * Protocol check succeeded, check power-on data if enabled
+ * RL-ATR-FILTER
+ * RL-SEL-USAGE.1
+ */
+ powerOnData = mReaderSpi->getPowerOnData();
+ if (checkPowerOnData(powerOnData, internalSelector)) {
+ /* No power-on data filter or power-on data check succeeded,
+ * select by AID if enabled */
+ const auto internalIsoCardSelector
+ = std::dynamic_pointer_cast(
+ cardSelector);
+ if (internalIsoCardSelector
+ && internalIsoCardSelector->getAid().size() != 0) {
+ fciResponse = selectByAid(internalIsoCardSelector);
+ const std::vector& statusWords
+ = cardSelectionRequest
+ ->getSuccessfulSelectionStatusWords();
+ hasMatched = std::find(
+ statusWords.begin(),
+ statusWords.end(),
+ fciResponse->getStatusWord())
+ != statusWords.end();
+ } else {
+ fciResponse = nullptr;
+ }
} else {
+ /* Check failed */
+ hasMatched = false;
fciResponse = nullptr;
}
} else {
- /* Check failed */
- hasMatched = false;
+ /* Protocol failed */
+ powerOnData = "";
fciResponse = nullptr;
+ hasMatched = false;
}
- } else {
- /* Protocol failed */
- powerOnData = "";
- fciResponse = nullptr;
- hasMatched = false;
- }
- return std::make_shared(
- powerOnData, fciResponse, hasMatched);
-}
+ return std::make_shared(
+ powerOnData, fciResponse, hasMatched);
-std::shared_ptr
-LocalReaderAdapter::processCardSelectionRequest(
- std::shared_ptr cardSelector,
- std::shared_ptr cardSelectionRequest)
-{
- mIsLogicalChannelOpen = false;
- std::shared_ptr selectionStatus = nullptr;
-
- try {
- selectionStatus = processSelection(cardSelector, cardSelectionRequest);
} catch (const ReaderIOException& e) {
throw ReaderBrokenCommunicationException(
std::make_shared(
@@ -361,6 +352,7 @@ LocalReaderAdapter::processCardSelectionRequest(
false,
e.getMessage(),
std::make_shared(e));
+
} catch (const CardIOException& e) {
throw CardBrokenCommunicationException(
std::make_shared(
@@ -369,7 +361,18 @@ LocalReaderAdapter::processCardSelectionRequest(
e.getMessage(),
std::make_shared(e));
}
+}
+std::shared_ptr
+LocalReaderAdapter::processCardSelectionRequest(
+ std::shared_ptr cardSelector,
+ std::shared_ptr cardSelectionRequest,
+ const ChannelControl channelControl)
+{
+ mIsLogicalChannelOpen = false;
+
+ std::shared_ptr selectionStatus(
+ processSelection(cardSelector, cardSelectionRequest));
if (!selectionStatus->mHasMatched) {
/* The selection failed, return an empty response having the selection
* status */
@@ -387,7 +390,8 @@ LocalReaderAdapter::processCardSelectionRequest(
if (cardSelectionRequest->getCardRequest() != nullptr) {
cardResponse
- = processCardRequest(cardSelectionRequest->getCardRequest());
+ = std::dynamic_pointer_cast(processCardRequest(
+ cardSelectionRequest->getCardRequest(), channelControl));
} else {
cardResponse = nullptr;
}
@@ -449,8 +453,10 @@ LocalReaderAdapter::processApduRequest(
* RL-SW-6CXX.1
* Update the last command with the provided "le"
*/
- apduRequest->getApdu()[apduRequest->getApdu().size() - 1]
+ std::vector apdu = apduRequest->getApdu();
+ apdu[apduRequest->getApdu().size() - 1]
= (apduResponse->getStatusWord() & SW2_MASK);
+ apduRequest->setApdu(apdu);
/* Replay the last command APDU */
apduResponse = processApduRequest(apduRequest);
@@ -481,66 +487,6 @@ LocalReaderAdapter::processApduRequest(
return apduResponse;
}
-std::shared_ptr
-LocalReaderAdapter::processCardRequest(
- const std::shared_ptr cardRequest)
-{
- std::vector> apduResponses;
-
- /* Proceeds with the APDU requests present in the CardRequest */
- for (const auto& apduRequest : cardRequest->getApduRequests()) {
- try {
- const auto apduResponse = processApduRequest(apduRequest);
- apduResponses.push_back(apduResponse);
-
- const std::vector& successfulSW
- = apduRequest->getSuccessfulStatusWords();
- if (cardRequest->stopOnUnsuccessfulStatusWord()
- && std::find(
- successfulSW.begin(),
- successfulSW.end(),
- apduResponse->getStatusWord())
- == apduRequest->getSuccessfulStatusWords().end()) {
- throw UnexpectedStatusWordException(
- std::make_shared(apduResponses, false),
- cardRequest->getApduRequests().size()
- == apduResponses.size(),
- "Unexpected status word");
- }
- } catch (const ReaderIOException& e) {
- /*
- * The process has been interrupted. We close the logical channel
- * and launch a KeypleReaderException with the Apdu responses
- * collected so far.
- */
- closeLogicalAndPhysicalChannelsSilently();
-
- throw ReaderBrokenCommunicationException(
- std::make_shared(apduResponses, false),
- false,
- "Reader communication failure while transmitting a card "
- "request",
- std::make_shared(e));
- } catch (const CardIOException& e) {
- /*
- * The process has been interrupted. We close the logical channel
- * and launch a KeypleReaderException with the Apdu responses
- * collected so far.
- */
- closeLogicalAndPhysicalChannelsSilently();
-
- throw CardBrokenCommunicationException(
- std::make_shared(apduResponses, false),
- false,
- "Card communication failure while transmitting a card request",
- std::make_shared(e));
- }
- }
-
- return std::make_shared(
- apduResponses, mIsLogicalChannelOpen);
-}
-
void
LocalReaderAdapter::releaseChannel()
{
@@ -627,17 +573,54 @@ LocalReaderAdapter::processCardRequest(
{
checkStatus();
- std::shared_ptr cardResponse = nullptr;
+ /* Proceeds with the APDU requests present in the CardRequest */
+ std::vector> apduResponses;
+
+ for (const auto& apduRequest : cardRequest->getApduRequests()) {
+ try {
+ const std::shared_ptr apduResponse(
+ processApduRequest(apduRequest));
+ apduResponses.push_back(apduResponse);
+ if (cardRequest->stopOnUnsuccessfulStatusWord()
+ && !Arrays::contains(
+ apduRequest->getSuccessfulStatusWords(),
+ apduResponse->getStatusWord())) {
+ if (channelControl == ChannelControl::CLOSE_AFTER) {
+ closeLogicalAndPhysicalChannelsSilently();
+ }
+ throw UnexpectedStatusWordException(
+ std::make_shared(apduResponses, false),
+ cardRequest->getApduRequests().size()
+ == apduResponses.size(),
+ "Unexpected status word");
+ }
+
+ } catch (const ReaderIOException& e) {
+ closeLogicalAndPhysicalChannelsSilently();
+ throw ReaderBrokenCommunicationException(
+ std::make_shared(apduResponses, false),
+ false,
+ "Reader communication failure while transmitting a card "
+ "request",
+ std::make_shared(e));
- /* Process the CardRequest and keep the CardResponse */
- cardResponse = processCardRequest(cardRequest);
+ } catch (const CardIOException& e) {
+ closeLogicalAndPhysicalChannelsSilently();
+ throw CardBrokenCommunicationException(
+ std::make_shared(apduResponses, false),
+ false,
+ "Card communication failure while transmitting a card request",
+ std::make_shared(e));
+ }
+ }
/* Close the channel if requested */
if (channelControl == ChannelControl::CLOSE_AFTER) {
releaseChannel();
}
- return cardResponse;
+ return std::make_shared(
+ apduResponses, mIsLogicalChannelOpen);
}
std::vector>
@@ -681,7 +664,7 @@ LocalReaderAdapter::processCardSelectionRequests(
++p.first, ++p.second) {
/* Process the CardRequest and append the CardResponse list */
const auto cardSelectionResponse
- = processCardSelectionRequest(*p.first, *p.second);
+ = processCardSelectionRequest(*p.first, *p.second, channelControl);
cardSelectionResponses.push_back(cardSelectionResponse);
if (multiSelectionProcessing == MultiSelectionProcessing::PROCESS_ALL) {
@@ -795,12 +778,18 @@ LocalReaderAdapter::ApduRequest::setInfo(const std::string& info)
return *this;
}
-std::vector
+const std::vector&
LocalReaderAdapter::ApduRequest::getApdu() const
{
return mApdu;
}
+void
+LocalReaderAdapter::ApduRequest::setApdu(const std::vector& apdu)
+{
+ mApdu = apdu;
+}
+
const std::vector&
LocalReaderAdapter::ApduRequest::getSuccessfulStatusWords() const
{
diff --git a/src/main/MonitoringState.cpp b/src/main/MonitoringState.cpp
index 3d9cf70..c5f0659 100644
--- a/src/main/MonitoringState.cpp
+++ b/src/main/MonitoringState.cpp
@@ -22,16 +22,16 @@ operator<<(std::ostream& os, const MonitoringState ms)
{
switch (ms) {
case MonitoringState::WAIT_FOR_START_DETECTION:
- os << "MONITORING_STATE = WAIT_FOR_START_DETECTION";
+ os << "WAIT_FOR_START_DETECTION";
break;
case MonitoringState::WAIT_FOR_CARD_PROCESSING:
- os << "MONITORING_STATE = WAIT_FOR_CARD_PROCESSING";
+ os << "WAIT_FOR_CARD_PROCESSING";
break;
case MonitoringState::WAIT_FOR_CARD_REMOVAL:
- os << "MONITORING_STATE = WAIT_FOR_CARD_REMOVAL";
+ os << "WAIT_FOR_CARD_REMOVAL";
break;
case MonitoringState::WAIT_FOR_CARD_INSERTION:
- os << "MONITORING_STATE = WAIT_FOR_CARD_INSERTION";
+ os << "WAIT_FOR_CARD_INSERTION";
break;
}
diff --git a/src/main/SmartCardServiceAdapter.cpp b/src/main/SmartCardServiceAdapter.cpp
index 4e1ae41..0f48446 100644
--- a/src/main/SmartCardServiceAdapter.cpp
+++ b/src/main/SmartCardServiceAdapter.cpp
@@ -292,16 +292,10 @@ std::shared_ptr
SmartCardServiceAdapter::findReader(const std::string& readerNameRegex) const
{
for (const auto& plugin : mPlugins) {
- for (const auto& reader : plugin.second->getReaders()) {
- try {
- if (StringUtils::matches(reader->getName(), readerNameRegex)) {
- return reader;
- }
- } catch (const PatternSyntaxException& e) {
- throw IllegalArgumentException(
- "readerNameRegex is invalid: " + e.getMessage(),
- std::make_shared(e));
- }
+ std::shared_ptr reader(
+ plugin.second->findReader(readerNameRegex));
+ if (reader != nullptr) {
+ return reader;
}
}
diff --git a/src/main/WaitForCardInsertionStateAdapter.cpp b/src/main/WaitForCardInsertionStateAdapter.cpp
index dc7845f..bfd57fb 100644
--- a/src/main/WaitForCardInsertionStateAdapter.cpp
+++ b/src/main/WaitForCardInsertionStateAdapter.cpp
@@ -24,10 +24,10 @@ WaitForCardInsertionStateAdapter::WaitForCardInsertionStateAdapter(
std::shared_ptr monitoringJob,
std::shared_ptr executorService)
: AbstractObservableStateAdapter(
- MonitoringState::WAIT_FOR_CARD_INSERTION,
- reader,
- monitoringJob,
- executorService)
+ MonitoringState::WAIT_FOR_CARD_INSERTION,
+ reader,
+ monitoringJob,
+ executorService)
{
}
@@ -42,8 +42,8 @@ WaitForCardInsertionStateAdapter::onEvent(const InternalEvent event)
{
mLogger->trace(
"Internal event [%] received for reader [%] in current state [%]\n",
- getReader()->getName(),
event,
+ getReader()->getName(),
getMonitoringState());
/* Process InternalEvent */
@@ -72,16 +72,6 @@ WaitForCardInsertionStateAdapter::onEvent(const InternalEvent event)
case InternalEvent::STOP_DETECT:
switchState(MonitoringState::WAIT_FOR_START_DETECTION);
break;
-
- case InternalEvent::CARD_REMOVED:
- /* The card has been removed during default selection */
- if (getReader()->getDetectionMode() == DetectionMode::REPEATING) {
- switchState(MonitoringState::WAIT_FOR_CARD_INSERTION);
- } else {
- switchState(MonitoringState::WAIT_FOR_START_DETECTION);
- }
- break;
-
default:
mLogger->trace("Event ignored\n");
break;
diff --git a/src/main/WaitForCardProcessingStateAdapter.cpp b/src/main/WaitForCardProcessingStateAdapter.cpp
index 3f25677..2c697e3 100644
--- a/src/main/WaitForCardProcessingStateAdapter.cpp
+++ b/src/main/WaitForCardProcessingStateAdapter.cpp
@@ -44,8 +44,8 @@ WaitForCardProcessingStateAdapter::onEvent(const InternalEvent event)
{
mLogger->trace(
"Internal event [%] received for reader [%] in current state [%]\n",
- getReader()->getName(),
event,
+ getReader()->getName(),
getMonitoringState());
/* Process InternalEvent */
diff --git a/src/main/WaitForCardRemovalStateAdapter.cpp b/src/main/WaitForCardRemovalStateAdapter.cpp
index d326108..8cbf5ad 100644
--- a/src/main/WaitForCardRemovalStateAdapter.cpp
+++ b/src/main/WaitForCardRemovalStateAdapter.cpp
@@ -42,8 +42,8 @@ WaitForCardRemovalStateAdapter::onEvent(const InternalEvent event)
{
mLogger->trace(
"Internal event [%] received for reader [%] in current state [%]\n",
- getReader()->getName(),
event,
+ getReader()->getName(),
getMonitoringState());
/* Process InternalEvent */
diff --git a/src/main/WaitForStartDetectStateAdapter.cpp b/src/main/WaitForStartDetectStateAdapter.cpp
index 2f7826c..41b268a 100644
--- a/src/main/WaitForStartDetectStateAdapter.cpp
+++ b/src/main/WaitForStartDetectStateAdapter.cpp
@@ -42,8 +42,8 @@ WaitForStartDetectStateAdapter::onEvent(const InternalEvent event)
{
mLogger->trace(
"Internal event [%] received for reader [%] in current state [%]\n",
- getReader()->getName(),
event,
+ getReader()->getName(),
getMonitoringState());
/* Process InternalEvent */
diff --git a/src/main/cpp/ExecutorService.cpp b/src/main/cpp/ExecutorService.cpp
index 721ab80..9ffa028 100644
--- a/src/main/cpp/ExecutorService.cpp
+++ b/src/main/cpp/ExecutorService.cpp
@@ -14,6 +14,9 @@
#include "keyple/core/service/cpp/ExecutorService.hpp"
#include
+#include
+#include
+#include
#include "keyple/core/service/AbstractObservableStateAdapter.hpp"
#include "keyple/core/util/cpp/Thread.hpp"
@@ -27,37 +30,43 @@ using keyple::core::service::AbstractObservableStateAdapter;
using keyple::core::util::cpp::Thread;
ExecutorService::ExecutorService()
-: mRunning(true)
+: mRunning(false)
, mTerminated(false)
{
- mThread = new std::thread(&ExecutorService::run, this);
}
ExecutorService::~ExecutorService()
{
- mRunning = false;
-
- while (!mTerminated) {
- Thread::sleep(10);
- }
+ shutdown();
}
void
ExecutorService::run()
{
- /* Emulates a SingleThreadExecutor (e.g. only one thread at a time) */
+ while (true) {
+ std::unique_lock lock(mMutex);
- while (mRunning) {
- if (mPool.size()) {
- /* Start first service and wait until completion */
- std::shared_ptr job = mPool[0];
- job->run();
+ // Wait until there's a job or the service is shutting down
+ mCondition.wait(lock, [this]{
+ return !mPool.empty() || !mRunning;
+ });
- /* Remove from vector */
- mPool.erase(mPool.begin());
+ // Check if we should terminate
+ if (!mRunning && mPool.empty()) {
+ break;
}
- Thread::sleep(100);
+ // Get the job and remove it from the pool
+ std::shared_ptr job = mPool.front();
+ mPool.erase(mPool.begin());
+
+ // Unlock the mutex before running the job
+ // This allows other threads to submit new jobs while one is being processed
+ lock.unlock();
+
+ if (!job->isCancelled()) {
+ job->run();
+ }
}
mTerminated = true;
@@ -66,28 +75,47 @@ ExecutorService::run()
void
ExecutorService::execute(std::shared_ptr job)
{
- mPool.push_back(job);
+ {
+ std::lock_guard lock(mMutex);
+ if (!mThread) {
+ mRunning = true;
+ mThread = std::unique_ptr(new std::thread(&ExecutorService::run, this));
+ }
+ mPool.push_back(job);
+ }
+ mCondition.notify_one();
}
std::shared_ptr
ExecutorService::submit(std::shared_ptr job)
{
- mPool.push_back(job);
-
+ execute(job);
+ std::lock_guard lock(mMutex);
return mPool.back();
}
void
ExecutorService::shutdown()
{
- mRunning = false;
+ {
+ std::lock_guard lock(mMutex);
+ if (!mThread) {
+ return;
+ }
+ mRunning = false;
+ }
- while (!mTerminated) {
- Thread::sleep(10);
+ mCondition.notify_one();
+
+ if (mThread->joinable()) {
+ mThread->join();
}
+
+ mThread.reset();
+ mTerminated = true;
}
} /* namespace cpp */
} /* namespace service */
} /* namespace core */
-} /* namespace keyple */
+} /* namespace keyple */
\ No newline at end of file
diff --git a/src/main/cpp/Job.cpp b/src/main/cpp/Job.cpp
index da2349a..de2958f 100644
--- a/src/main/cpp/Job.cpp
+++ b/src/main/cpp/Job.cpp
@@ -39,12 +39,12 @@ Job::cancel(const bool mayInterruptIfRunning)
"Unsupported value for mayInterruptIfRunning (true)");
}
+ mCancelled = true;
+
if (!isAlive()) {
return false;
}
- mCancelled = true;
-
return true;
}
diff --git a/src/main/mainpage.dox b/src/main/mainpage.dox
new file mode 100644
index 0000000..4dbccbb
--- /dev/null
+++ b/src/main/mainpage.dox
@@ -0,0 +1,23 @@
+/**
+ * @mainpage Keyple Service C++ Library
+ *
+ * @section overview Overview
+ *
+ * The **Keyple Service C++ Library** contains the Keyple core components,
+ * providing the fundamental services and orchestration layer for the
+ * Keyple middleware architecture.
+ *
+ * @section key_features Key Features
+ *
+ * - Core Keyple service components
+ * - Plugin and reader management
+ * - Card selection and transaction orchestration
+ * - Event-driven architecture support
+ * - Service layer coordination
+ *
+ * @section resources Resources
+ *
+ * - **Documentation**: [keyple.org](https://keyple.org)
+ * - **API Documentation**: [docs.keyple.org/keyple-service-cpp-lib](https://docs.keyple.org/keyple-service-cpp-lib)
+ * - **Examples**: [github.com/eclipse/keyple-cpp-example](https://github.com/eclipse/keyple-cpp-example)
+ */
diff --git a/src/test/LocalReaderAdapterTest.cpp b/src/test/LocalReaderAdapterTest.cpp
index e3277bd..c68b899 100644
--- a/src/test/LocalReaderAdapterTest.cpp
+++ b/src/test/LocalReaderAdapterTest.cpp
@@ -144,7 +144,7 @@ setUp()
EXPECT_CALL(*apduRequestSpi.get(), getInfo())
.WillRepeatedly(ReturnRef(info));
EXPECT_CALL(*apduRequestSpi.get(), getApdu())
- .WillRepeatedly(Return(ok_rapdu));
+ .WillRepeatedly(ReturnRef(ok_rapdu));
apduRequests.push_back(apduRequestSpi);
cardRequestSpi = std::make_shared();
@@ -680,7 +680,7 @@ TEST(LocalReaderAdapterTest, transmitCardRequest_shouldReturnResponse)
EXPECT_CALL(*readerSpi.get(), transmitApdu(_))
.WillRepeatedly(Return(responseApdu));
EXPECT_CALL(*apduRequestSpi.get(), getApdu())
- .WillRepeatedly(Return(requestApdu));
+ .WillRepeatedly(ReturnRef(requestApdu));
LocalConfigurableReaderAdapter localReaderAdapter(readerSpi, PLUGIN_NAME);
localReaderAdapter.doRegister();
@@ -710,7 +710,7 @@ TEST(LocalReaderAdapterTest, transmitCardRequest_isCase4)
= HexUtil::toByteArray("00009000");
EXPECT_CALL(*apduRequestSpi.get(), getApdu())
- .WillRepeatedly(Return(requestApdu));
+ .WillRepeatedly(ReturnRef(requestApdu));
EXPECT_CALL(*readerSpi.get(), transmitApdu(requestApdu))
.WillRepeatedly(Return(responseApdu));
EXPECT_CALL(*readerSpi.get(), transmitApdu(getResponseRApdu))
@@ -740,7 +740,7 @@ TEST(
EXPECT_CALL(*readerSpi.get(), transmitApdu(_))
.WillRepeatedly(Return(responseApdu));
EXPECT_CALL(*apduRequestSpi.get(), getApdu())
- .WillRepeatedly(Return(requestApdu));
+ .WillRepeatedly(ReturnRef(requestApdu));
EXPECT_CALL(*apduRequestSpi.get(), getSuccessfulStatusWords())
.WillRepeatedly(ReturnRef(resp));
EXPECT_CALL(*cardRequestSpi.get(), stopOnUnsuccessfulStatusWord())
@@ -768,7 +768,7 @@ TEST(
EXPECT_CALL(*readerSpi.get(), transmitApdu(_))
.WillRepeatedly(Throw(CardIOException("")));
EXPECT_CALL(*apduRequestSpi.get(), getApdu())
- .WillRepeatedly(Return(requestApdu));
+ .WillRepeatedly(ReturnRef(requestApdu));
LocalReaderAdapter localReaderAdapter(readerSpi, PLUGIN_NAME);
localReaderAdapter.doRegister();
@@ -792,7 +792,7 @@ TEST(
EXPECT_CALL(*readerSpi.get(), transmitApdu(_))
.WillRepeatedly(Throw(ReaderIOException("")));
EXPECT_CALL(*apduRequestSpi.get(), getApdu())
- .WillRepeatedly(Return(requestApdu));
+ .WillRepeatedly(ReturnRef(requestApdu));
LocalReaderAdapter localReaderAdapter(readerSpi, PLUGIN_NAME);
localReaderAdapter.doRegister();
diff --git a/src/test/mock/ApduRequestSpiMock.hpp b/src/test/mock/ApduRequestSpiMock.hpp
index 8ea0758..a989004 100644
--- a/src/test/mock/ApduRequestSpiMock.hpp
+++ b/src/test/mock/ApduRequestSpiMock.hpp
@@ -19,13 +19,16 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "keyple/core/util/cpp/Any.hpp"
#include "keypop/card/spi/ApduRequestSpi.hpp"
+using keyple::core::util::cpp::any;
using keypop::card::spi::ApduRequestSpi;
class ApduRequestSpiMock final : public ApduRequestSpi {
public:
- MOCK_METHOD(std::vector, getApdu, (), (const, override));
+ MOCK_METHOD(const std::vector&, getApdu, (), (const, override));
+ MOCK_METHOD(void, setApdu, (const std::vector&), (override));
MOCK_METHOD(
const std::vector&,
getSuccessfulStatusWords,
diff --git a/src/test/mock/ApduResponseApiMock.hpp b/src/test/mock/ApduResponseApiMock.hpp
index 94dd493..e0b6785 100644
--- a/src/test/mock/ApduResponseApiMock.hpp
+++ b/src/test/mock/ApduResponseApiMock.hpp
@@ -25,7 +25,7 @@ using keypop::card::ApduResponseApi;
class ApduResponseApiMock final : public ApduResponseApi {
public:
MOCK_METHOD((const std::vector&), getApdu, (), (const, override));
- MOCK_METHOD(
- (const std::vector), getDataOut, (), (const, override));
+ MOCK_METHOD(void, setApdu, ((const std::vector&)), (override));
+ MOCK_METHOD((std::vector), getDataOut, (), (const, override));
MOCK_METHOD(int, getStatusWord, (), (const, override));
};