Skip to content
Closed
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- `TaskCanceledException`: added constructor `(String message, Throwable cause)` for consistency
with the other exceptions of the package.
### Changed
- Migrated the CI pipeline from Jenkins to GitHub Actions.
- `ReaderSpi`: clarified the contracts of `openPhysicalChannel()`, `closePhysicalChannel()` and
`checkCardPresence()` regarding physical channel state management (see Javadoc for details).
- `ReaderSpi.getPowerOnData()`: documented lifecycle (meaningful only after a successful
`openPhysicalChannel()`) and allowed empty return value.
- `CardRemovalWaiterNonBlockingSpi`: the service now uses `checkCardPresence()` instead of
`transmitApdu()` for card removal polling. This change is conditioned by the plugin API version:
the new behaviour applies only when the plugin declares API version `2.4` or higher.
- Various Javadoc corrections and minor clarifications.
### Fixed
- `CardRemovalWaiterAsynchronousApi`: corrected `@link` pointing to a deprecated SPI.

## [2.3.2] - 2025-04-18
### Changed
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
group = org.eclipse.keyple
title = Keyple Plugin Java API
description = API dedicated to plugins development
version = 2.3.3-SNAPSHOT
version = 2.4.0-SNAPSHOT

# Java Configuration
javaSourceLevel = 1.8
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

/**
* API associated to a {@link
* org.eclipse.keyple.core.plugin.spi.reader.observable.state.removal.WaitForCardRemovalAutonomousSpi}
* org.eclipse.keyple.core.plugin.spi.reader.observable.state.removal.CardRemovalWaiterAsynchronousSpi}
*
* @since 2.2.0
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public final class PluginApiProperties {
*
* @since 2.0.0
*/
public static final String VERSION = "2.3";
public static final String VERSION = "2.4";

/** Private constructor */
private PluginApiProperties() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,13 @@ public class TaskCanceledException extends Exception {
public TaskCanceledException(String message) {
super(message);
}

/**
* @param message the message to identify the exception context
* @param cause the cause
* @since 2.3.3
*/
public TaskCanceledException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public interface AutonomousObservablePluginSpi extends PluginSpi {
/**
* Connects the associated Keyple Core {@link AutonomousObservablePluginApi} API.
*
* <p>This method is no longer called by the framework since version 2.2.0. Implementations
* migrating to {@link #setCallback(AutonomousObservablePluginApi)} may provide an empty body for
* this method.
*
* @param autonomousObservablePluginApi The API to connect.
* @since 2.0.0
* @deprecated Use {@link #setCallback(AutonomousObservablePluginApi)} instead.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
import org.eclipse.keyple.core.plugin.ReaderIOException;

/**
* Reader able to communicate with smart cards whose purpose is to remain present in the reader (for
* example a SAM reader).
* Reader able to communicate with smart cards.
*
* <p>This is the base interface for all reader types, including readers with permanently present
* cards (e.g. SAM readers) and observable readers detecting card insertion and removal.
*
* <p>The target devices must comply with the following Calypsonet Terminal requirements:
*
Expand All @@ -40,27 +42,36 @@ public interface ReaderSpi {
String getName();

/**
* Validates the opening of the physical channel. Performs the actual opening if this has not been
* done by the {@link #checkCardPresence()} method. In all cases, memorizes the new state for the
* operation of the {@link #isPhysicalChannelOpen()} method. After executing this method, the
* reader is able to send APDUs to the card.
* Ensures that the physical channel is open and that the card is ready to receive APDU commands.
*
* <p>On successful return:
*
* <ul>
* <li>the power-on data are available via {@link #getPowerOnData()};
* <li>the card is ready to receive APDU commands via {@link #transmitApdu(byte[])}.
* </ul>
*
* <p>If {@link #checkCardPresence()} has already opened the physical channel (e.g. for
* contactless readers performing anti-collision during presence detection), this method is a
* no-op.
*
* @throws ReaderIOException If the communication with the reader has failed.
* @throws CardIOException If the communication with the card has failed.
* @throws CardIOException If no card is present or if the communication with the card has failed.
* @since 2.0.0
*/
void openPhysicalChannel() throws ReaderIOException, CardIOException;

/**
* Tells the reader that card processing is complete and that the next step is to remove the card
* from the reader.
* Closes the physical channel.
*
* <p>If the reader has the ability to sense the presence of the card without communicating with
* it, then this method must proceed to the actual closing of the physical channel (e.g. power
* down in the case of a contact reader). Otherwise, this method is limited to changing the
* logical opening state of the physical channel and letting the removal procedure do the closing.
* <ul>
* <li><b>Card present:</b> physically closes the channel (e.g. cuts the RF field, powers down
* the card, or performs a PC/SC reset).
* <li><b>Card absent:</b> no-op.
* </ul>
*
* @throws ReaderIOException If the communication with the reader has failed.
* @throws ReaderIOException If the card is present and the close operation fails (reader
* problem).
* @since 2.0.0
*/
void closePhysicalChannel() throws ReaderIOException;
Expand All @@ -69,20 +80,26 @@ public interface ReaderSpi {
* Tells if the physical channel is open or not.
*
* @return True is the physical channel is open, false if not.
* @since 2.0.0
*/
boolean isPhysicalChannelOpen();

/**
* Verifies the presence of a card.
*
* <p>Depending on the reader's capabilities, this method will either use a card presence
* indicator without necessarily communicating with the card (for example, in the case of a
* contact reader equipped with a physical insertion detector using a switch), or will communicate
* with the card (in the case of contactless hunting). In the latter case, we can consider that
* the physical channel has been opened (and therefore no longer needs to be opened in the {@link
* #openPhysicalChannel()} method).
* <p>The behavior of this method depends on the state of the physical channel:
*
* <ul>
* <li><b>Physical channel closed:</b> performs a best-effort one-shot detection, starting the
* RF field or powering up the card if necessary. If this detection also opens the physical
* channel (e.g. for contactless readers performing anti-collision), a subsequent call to
* {@link #openPhysicalChannel()} is a no-op.
* <li><b>Physical channel open:</b> verifies that the card is still present using the
* underlying SDK capabilities (e.g. ping APDU). If the card is no longer present, {@link
* #closePhysicalChannel()} is called internally before returning {@code false}.
* </ul>
*
* @return True if a card is present
* @return {@code true} if a card is present, {@code false} otherwise.
* @throws ReaderIOException If the communication with the reader has failed.
* @since 2.0.0
*/
Expand All @@ -92,6 +109,7 @@ public interface ReaderSpi {
* Gets the power-on data.
*
* <p>The power-on data is defined as the data retrieved by the reader when the card is inserted.
* This method is only meaningful after {@link #openPhysicalChannel()} has returned successfully.
*
* <p>In the case of a contact reader, this is the Answer To Reset data (ATR) defined by ISO7816.
*
Expand All @@ -101,19 +119,23 @@ public interface ReaderSpi {
* the ISO14443 protocol (ATQA, ATQB, ATS, SAK, etc).
*
* <p>These data being variable from one reader to another, they are defined here in string format
* which can be either a hexadecimal string or any other relevant information.
* which can be either a hexadecimal string or any other relevant information. An empty string may
* be returned if no power-on data is available (e.g. for a reader with a permanently powered
* card).
*
* @return A not empty String.
* @return A non-null String, possibly empty.
* @since 2.0.0
*/
String getPowerOnData();

/**
* Transmits an APDU and returns its response.
*
* <p><b>Caution: the implementation must handle the case where the card response is 61xy and
* execute the appropriate get response command (Calypsonet Terminal requirement
* "RL-SW-61XX.1").</b>
* <p><b>Caution: the implementation must handle the ISO 7816-3 T=0 protocol specificity at the
* transport level: status word {@code 61xx} (response bytes available) requires automatically
* issuing a {@code GET RESPONSE} command (Calypsonet Terminal requirement "RL-SW-61XX.1"). This
* is handled at the SPI level because its behavior depends on the underlying reader
* implementation (T=0, T=1, PC/SC).</b>
*
* @param apduIn The data to be sent to the card.
* @return A buffer of at least 2 bytes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@

import org.eclipse.keyple.core.plugin.spi.reader.ReaderSpi;
import org.eclipse.keyple.core.plugin.spi.reader.observable.state.insertion.*;
import org.eclipse.keyple.core.plugin.spi.reader.observable.state.processing.*;
import org.eclipse.keyple.core.plugin.spi.reader.observable.state.removal.*;

/**
* Reader able to detect the insertion and removal of cards.
*
* <p>In addition, an observable reader must also define its observation capabilities for the card
* insertion and removal steps.
* insertion, removal, and optionally processing steps.
*
* <p>For the card insertion state, it must implement one of the following interfaces:
*
Expand All @@ -37,6 +38,18 @@
* <li>{@link CardRemovalWaiterNonBlockingSpi}
* </ul>
*
* <p>For the card processing state (monitoring card presence between APDU commands), it may
* optionally implement:
*
* <ul>
* <li>{@link CardPresenceMonitorBlockingSpi} for readers capable of blocking card presence
* monitoring (e.g. PC/SC readers).
* </ul>
*
* <p>Readers implementing {@link CardRemovalWaiterAsynchronousSpi} (e.g. Android NFC readers) do
* not need to implement {@link CardPresenceMonitorBlockingSpi}: the asynchronous removal callback
* covers all phases, including processing.
*
* @since 2.0.0
*/
public interface ObservableReaderSpi extends ReaderSpi {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@
public interface CardPresenceMonitorBlockingSpi {

/**
* Monitors the card presence indefinitely (the method is blocking as long as the card is
* present).
* Monitors the card presence indefinitely (the method is blocking as long as the card is present)
* and returns normally when the card is removed.
*
* <p>This monitoring can be cancelled for an internal (for example timeout) or external reason
* (for example invocation of {@link #stopCardPresenceMonitoringDuringProcessing()}), in this case
* an exception is raised.
* (for example invocation of {@link #stopCardPresenceMonitoringDuringProcessing()}), in which
* case an exception is raised.
*
* @throws ReaderIOException If the communication with the reader has failed.
* @throws TaskCanceledException If the task has been canceled and is no longer active.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public interface CardRemovalWaiterBlockingSpi {
* <p>This wait can be cancelled for an internal (for example timeout) or external reason (for
* example invocation of {@link #stopWaitForCardRemoval()}), in this case an exception is raised.
*
* @throws ReaderIOException If the communication with the reader
* @throws ReaderIOException If the communication with the reader has failed.
* @throws TaskCanceledException If the task has been canceled and is no longer active
* @since 2.2.0
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,28 @@
package org.eclipse.keyple.core.plugin.spi.reader.observable.state.removal;

/**
* This SPI is specifically designed for plugins that don't handle card removal autonomously but
* requires the sending of an APDU to detect the card removal.
* This SPI is specifically designed for plugins that don't handle card removal autonomously.
*
* <p>When a plugin implements this SPI, the {@link
* org.eclipse.keyple.core.plugin.spi.reader.ReaderSpi#transmitApdu(byte[])} method will be called
* org.eclipse.keyple.core.plugin.spi.reader.ReaderSpi#checkCardPresence()} method will be called
* periodically by the service when a card removal is expected. The card is considered removed when
* the transmission fails.
* {@code checkCardPresence()} returns {@code false} (which also triggers the internal closure of
* the physical channel).
*
* <p>The value returned by the {@link #getCardRemovalMonitoringSleepDuration()} will be used as an
* <p>The value returned by {@link #getCardRemovalMonitoringSleepDuration()} will be used as an
* argument to {@link Thread#sleep(long)} between two calls to {@link
* org.eclipse.keyple.core.plugin.spi.reader.ReaderSpi#transmitApdu}.
* org.eclipse.keyple.core.plugin.spi.reader.ReaderSpi#checkCardPresence()}.
*
* <p>A typical example of readers conforming to this mode of operation are terminals embedding a
* slave RF communication module without card presence feature.
* slave RF communication module without autonomous card presence detection.
*
* @since 2.2.0
*/
public interface CardRemovalWaiterNonBlockingSpi {

/**
* Provides the value of the sleep duration (in milliseconds) inserted between two calls to {@link
* org.eclipse.keyple.core.plugin.spi.reader.ReaderSpi#transmitApdu}.
* org.eclipse.keyple.core.plugin.spi.reader.ReaderSpi#checkCardPresence()}.
*
* @return A positive value (0 is allowed).
* @since 2.2.0
Expand Down
Loading
Loading