JEE 7 Batch Processing – partie #3

Nous avons vu dans la partie précédente les éléments principaux permettant la mise en œuvre d’un batch (step, transaction, gestions des erreurs et checkpoints).

Nous allons présenter ci-dessous la programmation avancée des batch avec l’api de batch de JEE7, ceci passant par la définition de listeners, par la mise en place de flots d’exécutions qui permettent notamment des branchements conditionnels,  des redirections, etc. Finalement nous aborderons le paramétrage avancé des étapes du batch pour fournir les paramètres nécessaire à nos différents éléments.

Les Listeners

Il est possible d’attacher des listeners à peu près pour chacun des éléments du batch parmi lesquels SepListener et ItemProcessListener.

StepListener

L’implémentation définit deux méthodes beforeStep et afterStep. Sa déclaration se fait au niveau de l’élément Step du fichier de configuration XML. Il peut être utilisé par exemple pour faire une action spécifique avant une étape et libérer des ressources en fin d’étape.

Exemple : affichage des informations sur l’exécution du Job.

public class InfoJobListener implements StepListener {

@Inject
StepContext<?, ?> stepCtx;

@Override
public void beforeStep() throws Exception {
System.out.println("Start Step " + stepCtx.getStepName());
}

@Override
public void afterStep() throws Exception {
System.out.println("End step");
}
}

 

<step id="chunck-step">
[…]
<listeners>
<listener ref="InfoJobListener"></listener>
</listeners>
[…]
</step>

ItemProcessListener

Il définit trois méthodes beforeProcess, afterProcess et onProcessError.

Les méthodes beforeProcess et afterProcess sont déclenchées avant et après le traitement d’un l’élément par le processor, la méthode onProcessError est appelée si le traitement de l’élément par le processor lève une exception.

Exemple :

public class FileItemProcessListener implements ItemProcessListener<String, LineItem> {

@Inject
StepContext<?, Serializable> stepContext;
@Override
public void beforeProcess(String item) throws Exception {
System.out.println("before processing step " + stepContext.getStepName());
}
@Override
public void afterProcess(String item, LineItem line) throws Exception {
System.out.println("after processing step " + stepContext.getStepName());
}
@Override
public void onProcessError(String item, Exception exception) throws Exception {
System.out.println("error processing item " + item + " in step " + stepContext.getStepName());
System.out.println(exception);
}
}

 

<step id="chunck-step">
[…]
<listeners>
<listener ref="FileItemProcessListener"></listener>
</listeners>
[…]
</step>

Flot d’exécution

L’élément suivant

Il est possible de chaîner des étapes en précisant l’étape suivante dans le fichier XML.

  • en définissant l’attribut next qui a pour valeur l’id de l’étape que l’on veut chaîner.
    <step id="chunck-step-1" next="chunck-step-2">
  • en utilisant l’élément next, sous-élément de step. Cet élément propose plusieurs attributs : on et to. L’attribut on définit pour quelle valeur de statut (positionné lors de la terminaison de l’étape), ou pour quelle  valeur renvoyée par le decider, la transition aura lieu. L’attribut to précise l’identifiant de la prochaine étape (qui peut être de type Step, flow ou split).
    <next on="FAILED" next="step-job-FAILED">

Les éléments <flow>, <split> et <decision>

L’attribut flow permet de définir un ensemble d’étapes qui s’exécutent de façon unitaire et séquentielle. Il peut contenir des sous éléments de type step, flow ou decision.

L’élément decision référence  une implémentation de Decider, elle expose une méthode :

public String decide(StepExecution[] executions) throws Exception

Selon la valeur retournée, il est possible de brancher la suite du traitement vers un Step ou un autre.

Exemple :

<flow>
<step id="step1">
[…]
</step>
<decision id="decision">
[…]
<next on="value1" to="step2"/>
<next on="value2" to="step3"/>
<stop on="value3" exit-status="status" />
</decision>
<step id="step2">
[…]
</step>
<step id="step3">
[…]
</step>
</flow>

L’élément split permet d’exécuter des séquences d’étapes (balise flow) de façon concurrente.

Exemple :

<split id="split1">
<flow id="flow1">
<step id="step1">
[…]
</step>
</flow>
<flow id="flow2">
<step id="step2">
[…]
</step>
</flow>
</split>

Partition des étapes

La balise partition permet de partitionner un Step. Cet élément est composé des sous-balises mapper ou plan et optionnellement des sous-balises collector, analyzer et reducer.

Le plan et le mapper permettent de définir des propriétés d’exécution. L’élément plan permet de configurer le nombre de threads, le nombre de partitions ainsi que des paramètres via le fichier XML.

plan partitions="2" threads="2">
<properties partition="0">
<property name="name0" value="value0"/>
</properties>
[…]
<properties partition="2">
<property name="name2" value="value2"/>
</properties>
</plan>

L’élément mapper définit un référence vers un objet de type PartitionMapper qui expose une méthode pour construire un PartitionPlan. Cet objet va contenir les propriétés relatives à chacune des partitions.

public class FileItemMapper implements PartitionMapper {

public PartitionPlan mapPartitions() throws Exception {

PartitionPlanImpl plan = new PartitionPlanImpl();
plan.setPartitions(2);
plan.setThreads(2);
Properties properties = new Properties();
properties.setProperty("fineName", "test.csv");
Properties[] partitionProperties = new Properties[2];

partitionProperties[0] = properties;
plan.setPartitionProperties(partitionProperties);

return plan;
}
}

L’élément collector récupère les données intermédiaires depuis une ou plusieurs partition(s), il les soumet à une instance de PartitionAnalyzer pour les traiter.

Une instance de PartitionReducer récupère les données intermédiaires qui seront ensuite agrégées.

Exemple :

<partition>
<collector ref="FileItemCollector"></collector>
<analyzer ref="FileItemAnalyzer"></analyzer>
<reducer ref="FileItemReducer"></reducer>
</partition>

Paramétrage

Récupération des propriétés

Les propriétés peuvent être récupérées au niveau du JobContext ou du StepContext, tout dépend à quel niveau sont définies les balises properties. Si elles sont définies directement en dessous de l’élément Job, elles sont globales à tous les Step, c’est alors le JobContext qui les contient. Sinon, elles sont locales au Step et sont dans l’objet StepContext courant.

Injection des contextes d’exécution

Il est possible d’utiliser CDI pour récupérer ces contextes en utilisant l’annotation @Inject :

@Inject
StepContext<?, ?> context;

@Inject
JobContext<?> jobContext;

L’accès aux propriétés se fait alors simplement par context.getProperties().

Passage de valeurs dans le contexte d’exécution

Les contextes exposent deux jeux de méthodes pour conserver des objets durant un Step (StepContext) ou un Job (JobContext).

Ces contextes proposent deux types de valeurs utilisateur : une qui ne sera pas sauvegardée (transient) et une qui sera enregistrée dans le checkpoint de l’étape (persistante). Il est alors possible de conserver des valeurs pendant un job, pendant un step ou même de récupérer les valeurs transient lors d’un nouveau lancement depuis un checkpoint donné.

public class StepListenerWithData implements StepListener {

private static final String FILE_NAME = "fileName";

@Inject
StepContext<Object, Serializable> context;

@Override
public void beforeStep() throws Exception {
Serializable nonTransientData = …;
Object transientData = …;
context.setPersistentUserData(nonTransientData);
context.setTransientUserData(transientData);
}
}

Les données du contexte seront disponibles durant l’exécution de cette étape via :

context.getTransientUserData();

et

context.gePersistentUserData();


Nous avons vu précédemment la structure complète d’un traitement batch, ainsi que l’importante palette d’outils qu’offre l’API JEE 7 pour la définition complète de ces traitements (step, listeners, point de sauvegarde, paramétrage, flots d’exécution…).

Nous allons voir dans la dernière partie à l’aide d’un exemple, comment mettre en œuvre notre traitement batch sur le serveur  d’application GlasshFish : démarrage de notre batch, suivi de son exécution au cours du temps. Nous verrons ainsi en situation réelle comment notre batch s’intègre totalement à cette version de Glassfish et comment il est possible d’examiner les détails de son exécution en  temps réel.

Expert Technique Java / JEE

0 commentaires

votre commentaire

Se joindre à la discussion ?
Vous êtes libre de contribuer !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Inscription newsletter

Ne manquez plus nos derniers articles !