Migration Guide - Keyple Java 0.8.1 to 0.9.0


This guide is intended to help a user of version 0.8.1 of Keyple Java to upgrade his application to the 0.9.0 version of the library.

From a user API point of view, the changes relate to the following topics:


Plugin registration in the SeProxyService

The registerPlugin method of the SeProxyService class now returns the reference of the registered plugin.

This makes it possible, for example, to perform a reader setup in an application such as this one:

   // Create a PcscPlugin and register it into the SeProxyService
   ReaderPlugin pcscPlugin = seProxyService.registerPlugin(new PcscPluginFactory());`

   // Get the PO reader from the plugin
   SeReader poReader = pcscPlugin.getReader("ASK LoGO 0");

Preparation of selection cases

The AidSelector, Selector and PoSelector classes now follow the Fluent Builder pattern for better handling of optional parameters.

The construction of an AidSelector is as follows:

AidSelector appAidSelector = AidSelector.builder()
                            .aidToSelect(AID)
                            .fileControlInformation(AidSelector.FileControlInformation.FCI)
                            .fileOccurrence(AidSelector.FileOccurrence.FIRST)
                            .build();

The fileControlInformation and fileOccurrence fields are optional (shown here with their default values), so a simple version can be :

AidSelector aidSelector = AidSelector.builder().aidToSelect(CalypsoClassicInfo.AID).build();

The construction of a SeSelector is as follows:

seSelector = SeSelector.builder()
                .seProtocol(SeCommonProtocols.PROTOCOL_ISO14443_4)
                .aidSelector(appAidSelector)
                .build();

The PoSelector adds the possibility to specify that an invalidated PO should be processed

seSelector = SeSelector.builder()
                .seProtocol(SeCommonProtocols.PROTOCOL_ISO14443_4)
                .aidSelector(appAidSelector)
                .invalidatedPo(InvalidatedPo.ACCEPT)
                .build();

The management of PO commands to be performed after the selection step (when it has been successful) is handled by the methods of the PoSelectionRequest class:

  • preparing to read files has been simplified and is done using the unique following method:
public void prepareReadRecordFile(byte sfi, int recordNumber)
  • preparing to select files is done using the following method:
public void prepareSelectFile(byte[] lid)
public void prepareSelectFile(short lid)

Note that from now the “prepare” methods no longer return indexes, the data will be placed in the CalypsoPo object.


Retrieving selection results

The MatchingSelection class no longer exists. In the class SelectionsResult (see processDefaultSelection/processExplicitSelection):

  • getActiveSelection is replaced by getActiveMatchingSe which returns an AbstractMatchingSe object (the still existing hasActiveSelection method must be used before)

  • getMatchingSelection is replaced by getMatchingSe which returns an AbstractMatchingSe object (may be null if the index provided does not correspond to a successful selection case)

  • getMatchingSelections now returns a Map containing a list of associated AbstractMatchingSe with the selection index that produced it (Map<Integer, AbstractMatchingSe>)

  • a new hasSelectionMatched method indicates whether the selection index provided corresponds to a successful selection case

  • a new getActiveSelectionIndex method returns the index of the active selection (the still existing hasActiveSelection method must be used before)


Definition of the security settings of the transaction

These parameters are defined via the PoSecuritySettings class, whose construction now follows the Fluent Builder pattern.

All parameters are optional except the SamResource.

Here is an example of a complete PoSecuritySettings build:

poSecuritySettings = new PoSecuritySettings.PoSecuritySettingsBuilder(samResource)
                            .sessionDefaultKif(AccessLevel.SESSION_LVL_PERSO, DEFAULT_KIF_PERSO)
                            .sessionDefaultKif(AccessLevel.SESSION_LVL_LOAD, DEFAULT_KIF_LOAD)
                            .sessionDefaultKif(AccessLevel.SESSION_LVL_DEBIT, DEFAULT_KIF_DEBIT)
                            .sessionDefaultKeyRecordNumber(AccessLevel.SESSION_LVL_PERSO, DEFAULT_KEY_RECORD_NUMBER_PERSO)
                            .sessionDefaultKeyRecordNumber(AccessLevel.SESSION_LVL_LOAD, DEFAULT_KEY_RECORD_NUMBER_LOAD)
                            .sessionDefaultKeyRecordNumber(AccessLevel.SESSION_LVL_DEBIT, DEFAULT_KEY_RECORD_NUMBER_DEBIT)
                            .sessionModificationMode(ModificationMode.ATOMIC)
                            .ratificationMode(RatificationMode.CLOSE_RATIFIED)
                            .sessionAuthorizedKvcList(authKvcs)
                            .build();

Creation of the PoTransaction object

Since PoSecuritySettings now integrates SamResource, the construction of PoTransaction has evolved slightly.

Here is an example:

PoTransaction poTransaction = new PoTransaction(new PoResource(poReader, calypsoPo), poSecuritySettings);

Transaction commands preparation

Just as with the “prepare” commands used for selection, the “prepare” commands used for transactions no longer return indexes.

The available commands in version 0.9 are:

public final void prepareSelectFile(SelectFileControl control)
public final void prepareSelectFile(short lid)
public final void prepareSelectFile(byte[] lid)
public final void prepareReadRecordFile(byte sfi, int recordNumber)
public final void prepareReadRecordFile(byte sfi, int firstRecordNumber, int numberOfRecords, int recordSize))
public final void prepareReadCounterFile(byte sfi, int countersNumber)
public final void prepareUpdateRecord(byte sfi, int recordNumber, byte[] recordData)
public final void prepareWriteRecord(byte sfi, int recordNumber, byte[] recordData)
public final void prepareAppendRecord(byte sfi, byte[] recordData)
public final void prepareIncreaseCounter(byte sfi, int counterNumber, int incValue)
public final void prepareDecreaseCounter(byte sfi, int counterNumber, int decValue)

Transaction commands processing

The “process” commands have also been revised and simplified.

They all return void.

In case of failure a exception is raised (see below).

public final void processOpening(PoTransaction.SessionSetting.AccessLevel accessLevel)

The ModificationMode is no longer required since it is integrated in the PoSecuritySettings.

Parameters previously used to specify that a file is read at login are removed.

Instead, the first prepareReadFile command will be automatically taken into account.

public final void processPoCommands()
public final void processPoCommandsInSession()
public final void processCancel(ChannelControl channelControl)
public final void processClosing(ChannelControl channelControl)

Retrieving data read from POs

This is a major evolution of the Keyple API. Previously, data read from Calyspo POs were retrieved by applications using “parser” methods.

With Keyple API 0.9, Calypso PO data is made available in the CalypsoPo object obtained during selection and enriched all along the operations performed with PoTransaction.

The public getter methods of CalypsoPo are:

public final String getDfName()
public final byte[] getDfNameBytes()
public final String getApplicationSerialNumber()
public final byte[] getApplicationSerialNumberBytes()

public final String getAtr()
public final String getStartupInfo()
public final PoRevision getRevision()
public final byte getSessionModification()
public final byte getApplicationType()
public final byte getApplicationSubtype()
public final byte getPlatform()
public final byte getSoftwareIssuer()
public final byte getSoftwareVersion()
public final byte getSoftwareRevision()

public final boolean isDeselectRatificationSupported()
public final boolean isConfidentialSessionModeSupported()
public final boolean isPublicAuthenticationSupported()
public final boolean isPinFeatureAvailable()
public final boolean isSvFeatureAvailable()

public final boolean isDfInvalidated()
public final boolean isDfRatified()

public final DirectoryHeader getDirectoryHeader()
public final ElementaryFile getFileBySfi(byte sfi)
public final ElementaryFile getFileByLid(short lid)
public final Map<Byte, ElementaryFile> getAllFiles()

Four new classes DirectoryHeader, ElementaryFile, FileHeader and FileData have been added.

DirectoryHeader

The public getters for this class are:

public short getLid()
public byte[] getAccessConditions()
public byte[] getKeyIndexes()

public byte getDfStatus()
public byte getKif(AccessLevel level)
public byte getKvc(AccessLevel level)

public String toString()
ElementaryFile

The public getters for this class are:

public byte getSfi()
public FileHeader getHeader()
public FileData getData()

public String toString()
FileHeader

The public getters for this class are:

public short getLid()
public int getRecordsNumber()
public int getRecordSize()
public FileType getType()
public byte getDfStatus()

public boolean isShared()
public Short getSharedReference()
public byte[] getAccessConditions()
public byte[] getKeyIndexes()

public String toString()
FileData

The public getters for this class are:

public byte[] getContent()
public byte[] getContent(int numRecord)
public byte[] getContent(int numRecord, int dataOffset, int dataLength)
public SortedMap<Integer, byte[]> getAllRecordsContent()

public int getContentAsCounterValue(int numCounter)
public SortedMap<Integer, Integer> getAllCountersValue()

public String toString()

So, for example to extract the contents of contract files present in the PO, the code might look like this:

[...]
    /* Read all 4 contracts command, record size set to 29 */
    poTransaction.prepareReadRecordFile(CalypsoClassicInfo.SFI_Contracts,
            CalypsoClassicInfo.RECORD_NUMBER_1, 4, 29);
    /* proceed with the sending of commands, don't close the channel */
    poTransaction.processPoCommandsInSession();

    ElementaryFile efContracts = calypsoPo.getFileBySfi(CalypsoClassicInfo.SFI_Contracts);

    SortedMap<Integer, byte[]> records = efContracts.getData().getAllRecordsContent();
    for (Map.Entry<Integer, byte[]> entry : records.entrySet()) {
        logger.info("Contract #{}: {}", entry.getKey(),
                ByteArrayUtil.toHex(entry.getValue()));
    }
[...]

Error handling

Since version 0.9, all Keyple exceptions are of the RuntimeException type.

Catching exceptions is therefore now optional.

However, it is possible to selectively catch certain exceptions in order to deal with particular cases.

The new hierarchy of Keyple exceptions is shown here

Previous