We apologize for untranslated text, you can use the Google Translation button to get an automatic translation of the web page in the language of your choice.

StoreMerge - Guide de l’utilisateur

Le StoreMerge est un outil fourni avec JCMS qui permet de fusionner deux fichiers store.xml.

Le StoreMerge est typiquement utilisé pour fusionner les données d’un environnement de développement avec les données d’un site en production.

Avec le script deploy.sh, le StoreMerge est le composant coeur du système de développement et de déploiement de JCMS.

La version du StoreMerge décrite dans cet article est celle fournie avec JCMS 8 SP1. Cette version peut aussi fusionner les stores JCMS 7.0 et JCMS 7.1.

1. Principes

Le StoreMerge prend deux fichiers store.xml, un ensemble de paramètres, des règles de fusion et produit un nouveau fichier store.xml contenant la fusion des deux premiers.

1.1 Fonctionnement général

Lorsque le StoreMerge est lancé, trois cas peuvent se présenter :

  1. Cas 1 : Aucun conflit n’a été détecté
    • La fusion a lieu
  2. Cas 2 : L'application des règles de fusion résout tous les conflits détectés
    • La fusion a lieu
    • Les éventuelles nouvelles opérations consécutives à la résolution des conflits sont ajoutées au store fusionné
  3. Cas 3 : L'application de ces règles ne résout pas tous les conflits
    • La fusion n'a pas lieu
    • La liste des conflits est indiquée
    • Un fichier de résolution spécifique est alimenté avec des règles par défaut pour chaque conflit détecté
    • Une fois qu’une règle de résolution a été choisie pour tous les conflits, il faut relancer le StoreMerge avec ce fichier

Fig 1. Principe de fonctionnement du StoreMerge

1.2 Identification des divergences

La fusion ne porte que sur les parties divergentes des deux stores. Pour cela, le StoreMerge doit connaître la plus grande estampille commune aux deux stores (GCS : Greatest Common Stamp). Toutes les opérations antérieures ou égales à cette estampille constituent le prefixe commun des deux stores. Toutes les opérations postérieures à cette estampille constituent le suffixe divergent. La fusion des deux store consiste donc à recopier dans le nouveau store le préfixe commun et à intégrer la fusion des deux suffixes divergents.

Le StoreMerge détermine automatiquement la GCS. Cependant, pour réduire le temps de traitement, notamment pour les stores de taille importante (plusieurs dizaines de Mo), il est recommandé d’indiquer au StoreMerge cette estampille (via le paramètre -gcs).

Note : Dans le cas où les préfixes précédant le GCS seraient différents, c'est le préfixe du store2 qui est utilisé pour créer le store fusionné.

1.3 Traitement des conflits

Les suffixes divergents de deux stores peuvent comporter des opérations conflictuelles. Par exemple, la même donnée (catégorie, groupe, publication, …) a été modifiée sur les deux stores.

Le StoreMerge résout automatiquement les conflits de mise à jour sur une même donnée lorsqu’il s’agit d’attributs différents. Par exemple, sur le store 1 le nom d’une catégorie a été modifié et le parent de la même catégorie a été modifié sur le store 2. Le store fusionné comportera ces deux modifications.

Lorsque le conflit porte sur le même attribut, le mode de résolution doit être indiqué au StoreMerge dans des fichiers de règles. De même, les conflits entre la mise à jour d’une donnée et sa suppression doivent aussi être renseignés dans ces fichiers.

Le StoreMerge reçoit deux fichiers de règles :

  1. Un fichier de règles générales que l’on constituera au fur et à mesure des conflits rencontrés. Ce fichier est optionnel.
  2. Un fichier de règles spécifiques à cette fusion qui contient tous les conflits qui n’ont pas été résolus par le fichier de règles générales. Ce fichier est obligatoire si des conflits ont été détectés.

Lorsque le StoreMerge identifie un conflit pour lequel aucune règle de résolution n’a été définie, il l’indique et, si l’option a été activée, alimente un fichier de règles avec ce conflit. Il suffit alors d’éditer ce fichier, de modifier éventuellement la résolution proposée et de relancer la fusion avec ce fichier.

2. Lancement du StoreMerge

Le StoreMerge est fourni dans jcms.jar. Il faut donc le lancer avec une JVM.

2.1 Lancement depuis Eclipse

Si vous disposez d’un environnement Eclipse, vous pouvez ajouter un lanceur pour le StoreMerge.

  1. Menu Run > Run Configuration...
  2. Sélectionnez Java Application
  3. Cliquez sur l’icône New
  4. Saisissez “StoreMerge” dans Name
  5. Remplissez l’onglet Main (cf. infra)
  6. Remplissez l’onglet Arguments avec vos options de lancement(cf. infra)
  7. Cliquez sur le bouton Apply

Pour lancer le StoreMerge, sélectionnez l’application StoreMerge et cliquez sur le bouton Run.

Fig 2. Lanceur pour Eclipse - Onglet Main

Fig 3. Lanceur pour Eclipse - Onglet Arguments

2.2 Lancement avec un script shell

Si vous êtes sur Unix/Linux, écrivez le script suivant et enregistrez le dans un fichier storemerge.sh.

#!/bin/sh
export JAVA_HOME=...
export JCMS_HOME=...
export CLASSPATH=$JCMS_HOME/WEB-INF/classes:$JCMS_HOME/WEB-INF/lib/*
$JAVA_HOME/bin/java -cp $CLASSPATH com.jalios.jcms.tools.StoreMerge $*

 

2.3 Options de lancement

La syntaxe minimale de lancement du StoreMerge pour fusionner le fichier store1.xml avec le fichier store2.xml et produire un nouveau fichier storeMerged.xml est la suivante :

storemerge.sh -output storeMerged.xml store1.xml store2.xml

 

La fusion échouera si un conflit est détecté. Pour produire le fichier de résolution, il faut ajouter l’option -prepareResolutionFile avec le chemin du fichier de résolution à créer.

storemerge.sh -prepareResolutionFile resolution.txt -output storeMerged.xml store1.xml store2.xml

 

Une fois le fichier de résolution défini il faut relancer le StoreMerge avec l’option -resolutionFile et le chemin du fichier de résolution.

storemerge.sh -resolutionFile resolution.txt -output storeMerged.xml store1.xml store2.xml

 

Lorsque vous aurez fait plusieurs fusions avec des résolutions de conflits, si vous identifiez des règles récurrentes, vous pouvez les enregistrer dans un fichier de règles générales et l’indiquez avec l’option -ruleFile.

storemerge.sh -ruleFile rules.txt -prepareResolutionFile resolution.txt -output storeMerged.xml store1.xml store2.xml

 

Si votre store est gros pensez à indiquer la plus grande estampille commune avec l’option -gcs. Celle-ci peut être obtenue par un premier lancement du StoreMerge, en consultant la sortie.

StoreMerge -gcs c_123456789 -ruleFile rules.txt -prepareResolutionFile resolution.txt -output storeMerged.xml store1.xml store2.xml

Note : Dans le cas où les préfixes précédant le GCS seraient différents, c'est le préfixe du store2 qui est utilisé pour créer le store fusionné.

 

D’autres options sont disponibles. Voici la syntaxe générale de lancement du StoreMerge :

usage: StoreMerge [options] -output storeMerged.xml
store1.xml store2.xml
options:
 -classicIO                     use the classic I/O (slower but consume less memory).
 -gcs <arg>                     provide the Greatest Common Stamp (exclusive with -useJStoreSyncOp option).
-ignoreConflicts               ignore the conflicts.
 -output <arg>                  the output file.
 -prepareResolutionFile <arg>   a file that will be filled with each conflicting ID.
 -resolutionFile <arg>          a file that contains a resolution for each conflicting ID.
 -ruleFile <arg>                a file that contains rules for conflict resolution.
 -strictUpdateConflicts         warn for each update/update conflict whatever the attribute values are.
 -useJStoreSyncOp               use the <JSTORE:SYNC> mark to find out the store divergence (exclusive with -gcs option).
exit code:
0: Merge done (or no merge to perform)
1: Bad parameter
2: Error during the merge
3: Conflicts have been detected

2.4 Exemple de lancement

Voici un exemple de sortie suite à un lancement du StoreMerge sur deux stores ayant des conflits :

 

INFO - store 1               : C:\Temp\store1.xml
INFO - store 2              : C:\Temp\store2.xml
INFO - ignoreConflicts     : false
INFO - strictUpdateConflicts : false
INFO - useJStoreSyncOp     : false
INFO - useClassicIO        : false
INFO - gcs                 : (no gcs)
INFO - output               : C:\Temp\storemerged.xml
INFO -ruleFile            :
INFO -resolutionFile      :
INFO -prepareResolutionFile : C:\Temp\resolution.txt
INFO - Start merging...
INFO - Load conflict resolution rules...
INFO - Compute store diff
INFO - Greatest Common Stamp: r_100
INFO - Load divergent suffixes...
INFO - Store 1 suffix: 11 operations on 11 data
INFO - Store 2 suffix: 4 operations on 4 data
INFO - Process conflicts...
WARN - Conflict update/update on data "r_11" (member) for attribute "declaredGroups":
# ============================================================
# Store 1 value: "@|r_1|r_2"
# ------------------------------------------------------------
# Store 2 value: "@|r_1|r_3|r_4"
# ------------------------------------------------------------
# Conflict details:
#  Store 1 new items: r_2
#  Store 2 new items: r_3, r_4
# ============================================================
WARN - Conflict update/update on data "r_1" (group) for attribute "name":
# ============================================================
# Store 1 value: "Group R1 - Updated by R"
# ------------------------------------------------------------
# Store 2 value: "Group R1 - Updated by C"
# ============================================================
WARN - 2 conflicts detected on 2 data. Stop merge.
WARN - The stores have not been merged (status: 3 - Conflicts).
INFO - Total time: 438 ms.

3. Résolution des conflits

A partir des deux stores à fusionner, le StoreMerge détermine les deux suffixes divergents.

Un conflit apparaît lorsqu'il existe pour une même donnée une liste d'opérations dans les deux suffixes divergents.

3.1 Types de conflits détectés

Les types de conflits sont nommés en fonction du type d'opération qui à eu lieu sur la donnée conflictuelle.

3.1.2 Conflits Update / Update

Conflit de mise à jour sur une même donnée.

Mode de résolution :

  • Fusion des attributs distincts.
  • Plusieurs règles sont proposées pour traiter les divergences sur un même attribut (cf. infra).
  • Des opérations additionnelles peuvent être ajoutées au store fusionné pour résoudre les conflits sur les attributs (cf. infra).

3.1.3 Conflits Update / Delete et Conflits Delete / Update

Conflit entre une mise à jour d'une donnée et la suppression de cette même donnée.

Mode de résolution :

  • Une seule des deux listes d'opérations est retenue.
  • Plusieurs règles sont proposées pour choisir quelle liste d'opérations retenir (cf. infra)

3.1.4 Conflits Create / Create

Conflit entre deux création d'une donnée. Chaque suffixe divergent comporte une opération de création sur une donnée avec le même identifiant.

Attention ! Ce type de conflit ne devrait pas apparaître. Il est révélateur d'une fusion manuelle du store (avec un éditeur de texte) ou d'un enchainement de fusion un peu particulier. Cela peut par exemple apparaitre si on essai de fusionner trois store s1, s2 et s3. On fusionne s1 et s2, ce qui produit s12. Puis s2 et s3, ce qui produit s23. Si on fusionne s12 et s23 alors ce type de conflit peut apparaitre car les suffix divergents peuvent comporter des opérations de s2.

Mode de résolution :

  • Une seule des deux listes d'opérations est retenue.
  • Plusieurs règles sont proposées pour choisir quelle liste d'opérations retenir (cf. infra)

3.1.5 Conflits Delete / Delete

Il ne s'agit pas véritablement d'un conflit puisque la donnée doit être supprimée. Cependant, on ne retient qu'une seule des deux listes d'opérations pour éviter d'avoir des messages d’erreur  au chargement du store dans le cas où il y aurait des opérations de mise à jour postérieures à la première des deux opérations de suppression.

Mode de résolution :

  • Seule la liste d'opérations du premier store fourni en paramètre est ajoutée au store fusionné.

3.1.5 Conflits non traités

Conflits inter-attributs

Les conflits portant sur des attributs distincts ne sont pas traités.

Exemple : Contrainte d'intégrité entre deux attributs (p. ex. : dans le type Article, champ photoCaption obligatoire si l'attribut photo n'est pas vide)

Conflits inter-données

Les conflits portant sur plusieurs données ne sont pas traités.

Exemple : Création d'un contenu avec la catégorie c1 vs. Suppression de la catégorie c1.

Recommandation

Après une fusion de store, il est recommandé de démarrer le site avec le store fusionné et de lancer le contrôle d'intégrité (Espace d'administration > Supervision > Contrôle d'intégrité des données).

3.2 Règles générales de résolution de conflits

Les règles de résolution de conflits sont de la forme :

Target: Action

Target : représente un moyen d’identifier une donnée conflictuelle

Action : représente le mode de résolution à appliquer.

 

3.2.1 Exemples de targets

generated.Article@categories  // l'attribut categories des Articles
generated.Portlet.*@skins     //  l'attribut skin de toutes les classes Portlet
workspace@administrators      // l'attribut administrators des Workspace
.*@titleML                    // l'attribut titleML de toutes les classes
generated.Portlet.*@.*        // Tous les attributs de toutes les portlet
$j_1@name:                    // l'attribut name de l'objet j_1
$j_2@.*:                      // tous les attributs de l'objet j_2

3.2.2 Exemples de règles

generated.Portlet.*@author:        ignore            // ignorer les conflits portant sur l'attribut author des Portlet
workspace.*@administrators:       ignore-order      // ignorer l'ordre des éléments du champ administrators des workspace
$j_1@name:                        store1            // pour les conflits portant sur le nom du groupe j_1, prendre les modification du store 1
$j_2@.*:                          store2            // pour tous les conflits portant sur le membre j_2, prendre les modification du store 2
Article@description:              prefer-not-empty  // pour les conflits portant sur le champ description des Article, prendre celui qui est rempli.
Article@description:              store1            // si la règle précédente n'a pas résolu le conflit, prendre la valeur du store1
generated.Portlet.*@update-delete: store2            // pour tous les conflits U/D sur les portlet, prendre l'opération du store 2
.*@update-delete:                  update            // pour tous les conflits U/D, privilégier le update

3.2.3 Syntaxe des règles

SyntaxeBNF des règles.

rule             ::= uuRule | udRule | ccRule
uuRule           ::= uuTarget ":" uuAction
uuTarget         ::= selector "@" attributSelector
selector         ::= classSelector | idSelector
idSelector       ::= "$" (regex | string)
classSelector    ::= regex | string
attributSelector ::= regex | string
uuAction         ::= "ignore" | "ignore-order" | "ignore-empty-item" | "merge" | "store1" | "store2" | "prefer-not-empty" | quoted-string
udRule           ::= udTarget ":" udAction
udTarget         ::= selector "@update-delete"
udAction         ::= "delete" | "update" | "store1" | "store2"
ccRule           ::= ccTarget ":" ccAction
ccTarget         ::= selector "@create-create"
ccAction         ::= "store1" | "store2"
quoted-string    ::= "\"" string "\""

3.2.4 Ordre des targets

Plusieurs targets peuvent cibler un même attribut. C'est l'ordre déclaré dans le fichier de résolution qui l'emporte. En cas de séquence d'action, elles sont jouées dans l'ordre de la séquence.

Aussi, il convient d'écrire les règles de la plus précise à la plus large.

Exemple

Règles :

generated.PortletRSS@displayTitle: store2
generated.PortletRSS@.*:           store1
generated.Portlet.*@.*[tT]itle.*: prefer-not-empty store2
generated.Portlet.*@.*:            store1

 

Résolutions :

Classe

Attribut en conflit

Targets

Actions

PortletRSS

displayTitle

generated.PortletRSS@displayTitle

store2

PortletRSS

title

generated.Portlet.*@.*[tT]itle.*

prefer-not-empty

PortletRSS

title

generated.Portlet.*@.*[tT]itle.*

store2

PortletRSS

author

generated.PortletRSS@.*

store1

PortletWorkflow

displayTitle

generated.Portlet.*@.*[tT]itle.*

generated.Portlet.*@displayTitle

prefer-not-empty

PortletWorkflow

displayTitle

generated.Portlet.*@.*[tT]itle.* , generated.Portlet.*@displayTitle

store2

PortletWorkflow

author

generated.Portlet.*@.*

store1

 

3.3 Actions sur les conflits Update/Update

3.3.1 ignore

Action : la fusion a lieu sans tenir compte des conflits sur cet attribut. Le résultat après fusion n’est pas prévisible. Il est lié à l’ordonnancement des opérations.

Génération d'opérations : non

Syntaxe : target: ignore

Exemple :

generated.Portlet.*@author: ignore // ignorer tout conflit sur l'attribut author des portlets
.*@opAuthor:                ignore // ignorer tout conflit sur l'attribut opAuthor de toute donnée

3.3.2 ignore-order

Action : la fusion a lieu sans tenir compte des conflits d'ordre sur cet attribut. Le résultat après fusion n’est pas prévisible. Il est lié à l’ordonnancement des opérations.

Génération d'opérations : non

Syntaxe : target: ignore-order

Exemple :

generated.Portlet.*@templates:       ignore-order
generated.Portlet.*@skins:           ignore-order
workspace.*@administrators:          ignore-order
workspace.*@typeMap:                 ignore-order
workspace.*@roleMap:                 ignore-order

3.3.3 ignore-empty-item

Action : la fusion a lieu sans tenir compte des valeurs vides dans cet attribut de type collection. Le résultat après fusion n’est pas prévisible. Il est lié à l’ordonnancement des opérations.

Génération d'opérations : non

Syntaxe : target: ignore-empty-item

Exemple :

generated.Portlet.*@childrenBindings: ignore-empty-item
generated.Portlet.*@children:         ignore-empty-item

3.3.4 store1

Action : la valeur du store1 est retenue pour cet attribut.

Génération d'opérations : oui

Syntaxe : target: store1

Exemple :

generated.Portlet@.*:           store1
group@(workspace|parentSet):    store1

3.3.5 store2

Action : la valeur du store2 est retenue pour cet attribut.

Génération d'opérations : oui

Syntaxe : target: store2

Exemple :

wkrole@.*:                      store2
generated.Article@.*:           store2
generated.Portlet@displayTitle: store2

3.3.6 prefer-not-empty

Action : si le conflit porte entre une valeur vide et une valeur non vide alors c'est la valeur non vide qui l'emporte. Si ce n'est pas le cas, c'est la règle suivante qui s'applique.

Génération d'opérations : oui

Syntaxe : target: prefer-not-empty

Exemple :

.*@titleML: prefer-not-empty

3.3.7 merge

Action : les 2 jeux d'items de l'attribut conflictuel sont fusionnés. Cette règle s'applique uniquement aux attributs de type tableau, Collection et Map. Dans tous les cas, les valeurs null et les doublons sont éliminés. L'ordre est aléatoire. Pour les collections et les tableaux, cette action doit donc être réservée à des champs multivalués dont l'ordre n'est pas explicite.

Génération d'opérations : oui

Syntaxe : target: merge

Exemple :

Portlet.*@skins: merge

Exemple de fusion :

Store1 : skins="@skin1||skin2"
Store2 : skins="@skin2|skin3"
Après fusion : skins="@skin1|skin2|skin3"

3.3.8 quoted-string

Action : la valeur de l'attribut conflictuel est forcée à la valeur indiquée entre double quotes. Ce type de sélecteur est plutôt destiné au fichier de règles spécifiques.

Génération d'opérations : oui

Syntaxe : target: "new value"

Exemple :

Portlet.*@opAuthor: "j_2"

3.4 Actions sur les conflits Update/Delete

3.4.1 update

Action : lors d'un conflit update/delete sur une donnée, seules les opérations provenant du store ayant fait l'update sont retenues.

Génération d'opération : non

Syntaxe : target: update

Exemple :

Article.*@update-delete: update

3.4.2 delete

Action : lors d'un conflit update/delete sur une donnée, seules les opérations provenant du store ayant fait le delete sont retenues.

Génération d'opération : non

Syntaxe : target: delete

Exemple :

Portlet.*@update-delete: delete

3.4.3 store1

Action : lors d'un conflit update/delete, seules les opérations du store1 sont retenues

Génération d'opération : non

Syntaxe : target: store1

Exemple :

Portlet.*@update-delete: store1

3.4.4 store2

Action : lors d'un conflit update/delete, seules les opérations du store2 sont retenues

Génération d'opération : non

Syntaxe : target: store2

Exemple :

Article.*@update-delete: store2

3.5 Actions sur les conflits Create/Create

3.5.1 store1

Action : lors d'un conflit create/create, seules les opérations du store1 sont retenues

Génération d'opération : non

Syntaxe : target: store1

Exemple :

Portlet.*@create-create: store1

3.5.2 store2

Action : lors d'un conflit create/create, seules les opérations du store2 sont retenues

Génération d'opération : non

Syntaxe : target: store2

Exemple :

Article.*@create-create: store2

3.6 Opérations de résolution des conflits

Pour les lignes comportant des conflits sur des attributs et pour lesquelles une résolution où la valeur de l'un des deux stores est privilégiée (i.e. store1, store2 ou prefer-not-empty) ainsi que pour l'action merge, il est nécessaire d'effectuer une fusion des attributs.

Ceci est fait en ajoutant à la fin du store fusionné des opérations représentant ces fusions.

L'URID storemerge est utilisé pour la construction des estampilles de ces nouvelles opérations.

Les opérations de fusion ne modifient pas l'attribut mdate.

Exemple

Store 1 :

<generated.PortletRSS stamp="s1_10000" op="update" id="s1_6000" displayTitle="My Title" cacheType="None"    skins="@skin1|skin2"   mdate="123456789000" />

Store 2 :

<generated.PortletRSS stamp="s2_10000" op="update" id="s1_6000" displayTitle=""         cacheType="Session" skins="@skin2||skin1|" mdate="123456789001" />

Rules :

generated.Portlet.*@displayTitle: store1
generated.Portlet.*@cacheType:    store2 generated.Portlet.*@skins:       ignore-order

Fusion :

<generated.PortletRSS stamp="s1_10000" op="update" id="s1_6000" displayTitle="My Title"  cacheType="None"     skins="@skin1|skin2"   mdate="123456789000" />
<generated.PortletRSS stamp="s2_10000" op="update" id="s1_6000" displayTitle="" cacheType="Session" skins="@skin2||skin1|" mdate="123456789001" />
<generated.PortletRSS stamp="storemerge_10001" op="update" id="s1_6000" displayTitle="My Title" cacheType="Session" />

3.7 Exemple de règles par défaut

Voici un exemple de fichier de règles générales de résolution de conflit.

# Group
group@name:              store1
group@lastLdapSynchro:   store1
group@parentSet:         merge
group@order:             store1
group@rightFromClassMap: merge

# Workspace
workspace@typeMap: merge
# Portlet
generated.Portlet.*@displayTitle: prefer-not-empty
generated.Portlet.*@displayTitle:  store1
generated.Portlet.*@invalidClass:  merge
generated.Portlet.*@templates:     merge
generated.Portlet.*@skins:         merge
generated.Portlet.*@condition:     merge
generated.Portlet.*@.*:            store2
# Publication
.*@pdate:    store1
.*@edate:    store1
.*@adate:    store1
.*@sdate:    store1
.*@opAuthor: store1

In brief...

Le StoreMerge est un outil fourni avec JCMS qui permet de fusionner deux fichiers store.xml. Le StoreMerge est typiquement utilisé pour fusionner les données d’un environnement de développement avec les données d’un site en production. Avec le script deploy.sh, le StoreMerge est le composant coeur du système de développement et de déploiement de JCMS. La version du StoreMerge décrit dans cet article est celle fourni avec JCMS 8 SP1. Cette version peut aussi fusionner les stores JCMS 7.0 et JCMS 7.1.

Subject
Products
Published

6/23/15

Writer
  • Olivier Dedieu