Skip to content

Tutoriel développeur : créer un composant de A à Z.

Floww edited this page Mar 26, 2014 · 43 revisions

Créer un composant de A à Z

  1. Conception : comment organiser son composant ?
  2. Implémentation : comment créer une stratégie ?
  3. Test : comment tester une stratégie ?
  4. Intégration : comment ajouter son composant à la bibliothèque ?
  5. Documentation : comment documenter mon composant et ma stratégie ?

1 . Conception : réfléchir avant d'agir !

La première étape de la création d'un composant est de réfléchir à son organisation et son potentiel découpage. Le découpage d'un composant peut être facultatif, il dépend des attentes et des objectifs que l'on se fixe, il est donc important de réfléchir à ce que l'on souhaite avant de se lancer dans le développement.


Découpage

L'intérêt du découpage est d'affiner le détail de la simulation et de répartir les responsabilités de chaque élément pour mieux répartir l'implémentation au passage. Encore une fois, si la simulation est destinée à un large public non expert, on privilégiera un scénario simple donc avec peu de détail et de complexité. Si la simulation est destinée à un public expert, il sera cette fois-ci plus intéressant de détailler pour en tirer un intérêt de la simulation.

Exemple Une carte à puce peut être vue comme un composant abstrait "Carte" contenant une puce et une bande magnétique. La puce pourrait alors avoir un niveau de détail supplémentaire avec les applications "Visa", "CB", "Mastercard", "Moneo", ...


Organisation

Pour chaque composant identifié lors du précédent découpage, il faut maintenant réfléchir au type de composant qui sera nécessaire pour l'implémentation.

Voici les 3 types de composant :

  • ComponentI : entité passive qui réagit un message
  • ComponentO : entité active qui réagit à un évènement
  • ComponentIO : combo des capacités de Input et Output

Exemple Une puce réagit à des messages provenant d'un lecteur de carte à puce.




2 . Implémentation : comment créer une stratégie ?

Il faut maintenant implémenter le comportement de nos composants afin de reproduire le scénario imaginé au travers d'une stratégie.

Conseils d'implémentation

Conseil 1 : Une stratégie n'a pas de notion d'état, elle est juste l'implémentation déléguée du composant : il ne faut donc pas utiliser les attributs de classe mais les propriétés du composant (component.getProperties()).

Conseil 2 : Mettre les clés des propriétés en tant que constante de la classe.

Exemple public static final String CKEY_ACQUIRER_ID = "acquirer_id";


Interface IStrategy

Pour créer une stratégie, créer une classe implémentant l'interface IStrategy.

Définition des propriétés

public List<PropertyDefinition> getPropertyDefinitions();

Permet à la stratégie de communiquer les propriétés qu'elle utilise au composant (porteur des propriétés).

Exemple

ArrayList defs = new ArrayList();

defs.add(new PropertyDefinition(CKEY_ACQUIRER_ID, "", true, "Identifiant de l'acquéreur"));

defs.add(new PropertyDefinition(CKEY_ACCEPTOR_TERMINAL_ID, "", true, "Identifiant du système d'acceptation"));

defs.add(new PropertyDefinition(CKEY_ACCEPTOR_TERMINAL_ID, "", true, "Identifiant de l'accepteur"));

defs.add(new PropertyDefinition(CKEY_MERCHANT_CATEGORY_CODE, "", false, "Code de catégorie de marchandise"));

defs.add(new PropertyDefinition(CKEY_CURRENCY_CODE, "", true, "Code de la devise utilisée"));

return defs;

Initialisation

public void init(IOutput _this, Context ctx);

Permet à la stratégie de s'initialiser avant le lancement de la simulation. Voici les opérations à réaliser à cette étape :

  • s'enregistrer à un évènement
  • remettre à zéro des propriétés avant qu'elle soit demandée à la simulation (exemple : pin)

@Override

public void init(IOutput _this, Context ctx) {

// enregistrement aux évènements suivants

ctx.subscribeEvent(_this, "SMART_CARD_INSERTED");

ctx.subscribeEvent(_this, "REMOTE_DATA_COLLECTION");

((ComponentIO) _this).getProperties().put("pin_enter", null, true);

((ComponentIO) _this).getProperties().put("amount", null, true);

}

Traitement d'un évènement

public void processEvent(T _this, String event);

Permet de traiter un évènement "event". Les évènements de la simulation dépendent des points de démarrage configurés dans le simulateur.

@Override

public void processEvent(ComponentIO _this, String event) {

switch (event) {

case "SMART_CARD_INSERTED":

// implémentation

break;

}

}

Traitement d'un message

public IResponse processMessage(T _this, Mediator mediator, String data);

Permet de traiter un message entrant provenant d'un autre composant.

@Override

public IResponse processMessage(ComponentIO chip, Mediator m, String data) {

// parse du message entrant, on a du ISO 7816 ...

ISOMsg msg = ISO7816Tools.read(data);

// impl ...

// message réponse avec resData, chaine de réponse (éventuellement un pack() d'un ISOMsg).

return DataResponse.build(m, resData);

}


Appel d'un composant : utilisation des médiateurs

Dans le cas d'un traitement d'un évènement ou d'un message nous pouvons être amené à appeler un autre composant. Pour cela il faut utiliser les médiateurs par le biais de la classe Context.

Récupération d'un médiateur avec un composant d'un type donné

// récupération d'un médiateur

Mediator m = Context.getInstance().getFirstMediator(_this, ComponentEP.CARD.ordinal());

// envoi de données

m.send(_this, data);

Pour plus de clarté dans le code, nous conseillons d'utiliser les énumérations pour gérer les types de composant mais il est possible d'utiliser les entiers directement (ComponentEP.CARD.ordinal()).

Transfert d'un message à un sous-composant

Pour gérer les cas de composant abstrait telle qu'une carte, on veut transférer automatiquement les messages à la puce.

// récupération du composant enfant

Component chip = Component.getFirstChildType(card, ComponentEP.CARD_CHIP.ordinal());

// récupération médiateur de transfert

Mediator m_card_chip = MediatorFactory.getInstance().getForwardMediator(m, (IInput) chip);

// envoi des données

m_card_chip.send(card, data);

Utilisation de la console

Si vous souhaitez faire apparaître des messages dans la console vous devez utilisé l'interface de journalisation SLF4J.

Pour cela, définir un logger en attribut si ce n'est déjà fait (remplacer MaClasse par votre classe)

private static Logger log = LoggerFactory.getLogger(MaClasse.class);

Puis journalisez les évènements que vous souhaitez

log.debug(LogUtils.MARKER_COMPONENT_INFO, "Mon composant vient de recevoir un message : "+data);




3 . Test : comment tester une stratégie ?

Les composants s'adaptent bien aux développements avec les tests unitaires. Cela permet de tester son composant en cours de développement dans un premier temps et d'assurer la détection de régression dans un second temps. Les tests unitaires doivent être rédigés de façon segmentée afin de tester chaque fonction indépendamment efficacement.

Conseils

  • Définir les composants en attribut
  • Initialiser les composants (nom, strategie, parenté, propriétés, ...) dans une méthode statique annotée @Before
  • Rédiger chaque test dans une méthode annotée de @Test
  • Nettoyer le contexte dans une méthode statique annotée de @After

Exemple

public class FOAcquirerRemoteDataCollectionUnitTest {

private static ComponentIO frontOffice;

private static ComponentIO acquirer;

private static ComponentIO remoteDataCollection;

@Before

public void init() throws Exception {

Context.getInstance().autoRegistrationMode();

/* Init component */

frontOffice = new ComponentIO("Front Office", ComponentEP.FRONT_OFFICE.ordinal());

acquirer = new ComponentIO("Acquirer", ComponentEP.FO_ACQUIRER.ordinal());

remoteDataCollection = new ComponentIO("Remote Data Collection",

ComponentEP.FO_ACQUIRER_REMOTE_DATA_COLLECTION.ordinal());

/* Settings kinship */

frontOffice.addChild(acquirer);

acquirer.addChild(remoteDataCollection);

/* Settings strategies */ frontOffice.setStrategy(new FOStrategy());

acquirer.setStrategy(new FOAcquirerStrategy());

remoteDataCollection.setStrategy(new FOAcquirerRemoteDataCollectionStrategy());

}

@After

public void clean() throws Exception {

Context.getInstance().reset();

}

@Test

public void testSignOn() throws ISOException {

// sign on 0804

IResponse res = remoteDataCollection.notifyMessage(null, msg0804());

Assert.assertFalse(res.isVoid());

try {

ISOMsg msg = ISO8583Tools.read_CB2A_TLC(((DataResponse) res).getData());

Assert.assertEquals(msg.getMTI(), "0814");

Assert.assertEquals(msg.getValue(39), "0000");

}

catch (ISO8583Exception | ISOException e) {

Assert.assertTrue(false);

e.printStackTrace();

}

}

}




4 . Intégration : comment ajouter son composant à la bibliothèque ?

Regénération de l'application

Il faut re-générer l'application avec les modifications de la couche monétique. Si vous êtes dans Eclipse, cette opération sera effectuée automatiquement ; en dehors, il vous faudra utiliser Maven. Dans une invite de commandes, positionnez-vous dans le référentiel simulator et exécuter les commandes suivantes :

  • cd simulator
  • mvn clean install
  • cd ../simulator-ep
  • mvn clean install
  • cd ../gui-simulator
  • mvn clean install
  • cd build
  • java -jar gui-simulator-ep-1.0.0.jar

Vous pouvez copier gui-simulator-ep-1.0.0.jar dans votre répertoire livrable pour remplacer l'ancienne version où se trouve icônes, documentations et la librairie (comme défini dans le guide d'utilisateur).

Ajout d'un composant de niveau 1 (pas d'enfant)

Dans le cas d'un composant simple sans enfants, vous pouvez l'ajouter depuis l'interface graphique via le menu Atelier > Nouveau composant. Il suffit de ...

  • Nommer votre composant
  • Définir l'identifiant de classification : l'énumération ComponentEP s'en charge dans la couche monétique. On utilise alors simplement la position dans l'énumération comme identifiant. Si vous n'avez pas besoin de dissocier des types de composants, mettez 0.
  • Le type de composant : Voir point 1.Conception/Organisation.

Ajout d'un composant multi-niveaux (avec des enfants)

L'interface graphique n'étant pas totalement terminée, la fonction d'ajout d'un enfant à un composant n'est pas gérée de manière fonctionnelle. Pour parer cette limite, vous devez générer votre composant depuis une classe tiers en prenant exemple sur fr.ensicaen.simulator_ep.utils.GenerateBaseComponents.

Supprimez le dossier library dans le projet simulator-ep (ou celui de votre surcouche) puis définissez votre composant via le code Java dans une méthode main :

ComponentIO card = new ComponentIO("Card", ComponentEP.CARD.ordinal());

ComponentIO chip = new ComponentIO("Chip", ComponentEP.CARD_CHIP.ordinal());

chip.getProperties().put("protocol", "ISO7816");

chip.getProperties().put("pan", "4976710025642130");

ComponentIO magstrippe = new ComponentIO("Magstrippe", ComponentEP.CARD_MAGSTRIPPE.ordinal());

magstrippe.getProperties().put("iso2", "59859595985888648468454684");

card.addChild(magstrippe);

card.addChild(chip);

Créez le composant parent via la DAO :

DAO comp = DAOFactory.getFactory().getComponentDAO();

comp.create(card);

Exécutez la classe Java dans Eclipse, et rafraîchissez le projet de votre surcouche (clic droit > Refresh). Dans le dossier library/model, copiez le fichier qui s'est créé et coller-le dans votre librairie (dossier library/model à côté de votre archive Java (JAR) que vous avez régénéré en première partie.




5 . Documentation : comment documenter mon composant et ma stratégie ?

La documentation pour aider dans l'utilisation des composants et des stratégies est directement intégrée et intégrée dans l'application (panel de droit, onglet "détails", bouton "informations"). Cette documentation se rédige en externe au format HTML.

Dans le dossier "components", les documentations des composants et dans "strategies", les documentations des stratégies. Vous devez utiliser l'identifiant de classification en nom de fichier (avec l'extension .html) ou le nom de votre composant/stratégie sachant que le nom est prioritaire sur la classification.

La documentation doit être pertinente :

  • Enumération des cas gérés
  • Paramétrage général du composant
  • Evènement(s) géré(s)
  • Protocole(s) utilisé(s)
  • Composant(s) attendu(s)