Bienvenue

Jalios Community

Tout ce que vous souhaitez savoir sur l'écosystème Jalios

JPlatform 10 - Les nouveautés techniques back-end
JPlatform 10
Olivier Dedieu
November 23, 2017 462 vues
In brief...

Cette page présente les principales nouveautés de JPlatform 10 concernant les développements back-ends.


1. API

1.1 Préférences Utilisateurs (MemberPreference)

Certaines fonctions de JPlatform (portées par coeur ou par les modules) proposent à l'utilisateur des options qui peuvent être persistées.

Par exemple, la nouvelle Topbar de JPlatform 10 propose le menu "Applications" dans lequel l'utilisateur peut choisir ses applications préférées et l'ordre dans lequel elles apparaissent dans le menu.

Jusqu'à présent les développeurs devaient gérer eux même un mode de stockage spécifique pour ces préférences utilisateur (ExtraDBData ou type dédié).

JPlatform 10 fournit désormais une API homogène et performante pour le stockage des préférences utilisateurs.

L'article JPlatform 10 - API des préférences utilisateurs (MemberPreference) présente en détail cette nouvelle API.

1.2 Evolutions sur les DBPublication

1.2.1 DB Publication multilingues

Avec JPlatform 10 un type de publication stockés en base de données peut désormais avoir des champs multilingues. Il dispose ainsi de la plupart des capacités multilingues des publications du store. Le multilinguisme peut s'appliquer sur des champs mono-valués ou multi-valués. Les publication en base ont maintenant un champ "Langue principale". Enfin, elles disposent aussi des copies de traduction.

Il y a néanmoins quelques limites :

  • Seuls les champs multivalués de type List peuvent être multilingues (ce n'est pas possible pour les champs de type Set)
  • Le tri sur le titre ne porte que sur la langue du site. Il ne peut pas porter sur la langue de navigation
  • Lors de la comparaison de 2 versions d'une publication, le détail de changement sur les champs multivalués (monolingue ou multilingue) n'apparait pas.

En standard, les types suivants disposent de champs multilingues :

  • DBDocument (titre et description)
  • Media (titre et description)
  • DBWebPage (titre et description)

C'est aussi le cas de certains types livrés par des modules

  • Module Blog
    • Billet
  • Module JLearn
    • JLearn - Savoir
    • JLearn - Cours
    • JLearn - Parcours

Techniquement, les textes dans les langues additionnelles d'un champ multilingue sont sérialisés en JSON et stockés dans un champ dédié. Ce champ reprend le nom du champ texte associé avec le suffixe MLE (pour MultiLingual Encoding). Par exemple, au champ title est associé le champ titleMLE contenant une Map JSON contenant le titre dans les langues additionnelles.

1.2.2 Copie de travail pour les DB Content

Les contenus stockées dans la base de données disposent désormais de la fonction "copie de travail".

3 champs sont utilisées pour les copies de travail

  • mainInstance : renseigné dans la copie de travail et pointe l'instance originale
  • mergeId : renseigné sur l'instance originale après la fusion d'une copie de travail. Il sert dans l'affichage de l'historique des versions pour consulter l'historique de la copie ayant servie à la fusion
  • mergeDate : date de la dernière fusion.

Ces 3 champs sont ajoutés aux HBM des DB Content ainsi qu'aux HBM de leur révision.

1.2.3 FriendlyURL pour les DB Content

Les contenus en base disposent dans JPlatform 10 des URLs intuitives (friendly URL) comme pour les contenus stockés dans JStore.

1.2.4 QueryFilter pour les DBPublication

Dans le cas des publications du Store, les QueryFilter s'appliquent sur l'ensemble des résultats. Ce mode de fonctionnement n'est pas adapté aux contenus en base puisqu'il intervient après que la requête ait eu lieu et n'appliquerait donc qu'aux seuls les résultats de la tranche paginée.

A l'instar des RightPolicyFilter, il convient donc d'appliquer le filtrage durant l'itération de recherche (scroll). Ce mode de fonctionnement ayant un coût sur la recherche, il ne doit être enclenché que si des QueryFilter sont susceptibles d'intervenir sur cette recherche.

Pour le savoir, le QueryFilter doit l'indiquer au travers de la méthode callFilterDBResult() de la classe =QueryFilter. Cette méthode est appellée au début de la recherche en base afin de déterminer si au moins un QueryFilter souhaite participer à la recherche. Ceci imposera une recherche avec scrolling (comme dans le cas des RightPolicyFilter).

En complément, et pour des cas simple, la méthode = callFilterDBResult(Class clazz, Map<String,Object> context)= est fournie lorsque le contrôle se base simplement sur le type de publication.

Enfin, la méthode filterDBResult est appelée pour chaque résultats remontée de la recherché afin de déterminer si il doit être gardé ou non.

En synthèse, voici donc les méthodes qui ont été ajouté à la classe QueryFilter :

public abstract class QueryFilter {
   ...
   boolean callFilterDBResult (PublicationCriteria);
   boolean callFilterDBResult(Class clazz, Map<String,Object> context);
   boolean filterDBResult(Publication pub, Map<String,Objet> context)
}

1.3 Détection automatique du type de document

Dans le cadre de la prise en charge de volumétrie de données importantes, l'une des pistes consiste à répartir ces données dans différentes table de JcmsDB. En effet, moins une table contient de données plus les performances sont bonnes.

Traditionnellement, cette répartition en table peut être prise en charge directement par la base de données avec la technique du table partionning. Cependant, JPlatform 10 utilise l'ORM Hibernate pour le stockage des données en base, ce qui rend cette pratique compliquée à mettre en oeuvre.

L'alternative consiste donc à faire la répartition depuis JPlatform. On constate généralement que ce sont les documents qui représentent une grand part de la volumétrie à prendre en charge. Parmi eux, les média (images, video, audio) représentent une part importante. Pouvoir répartir ces média dans une table dédié est donc souhaitable.

Pour répondre à cet objectif, JPlatform 10 introduit la sélection automatique du type de document lors des dépôts ainsi que le type Media dédié au stockage des médias (images, video, audio).

En plus des bénéfices sur les performances, la sélection automatique du type de document aiderai encore d'avantage les utilisateurs qui sont amenés à déposer des documents en ne ce souciant plus du type à utiliser.

La sélection automatique peut s'appliquer à d'autres type que les documents.

1.3.1 Principes

La sélection automatique du type de document prend 3 paramètre :

  • Le loggedMember
  • L'espace cible
  • le MIME Type du fichier déposé

L'algorithme de sélection du type de document est la suivant :

  • On recherche si il existe un type de document associé au type MIME du fichier (eg. image/jpeg)
  • Si c'est le cas et si le loggedMember peut publier ce type dans dans l'espace cible alors on retient ce type
  • Sinon on recherche si il existe un type de document associé au top-level type MIME du fichier (eg. image)
  • Si c'est le cas et si le loggedMember peut publier ce type dans dans l'espace cible alors on retient ce type
  • Sinon si le loggedMember peut publier le type de document par défaut dans dans l'espace cible alors on retient ce type
  • Sinon si le loggedMember peut publier le type DBFileDocument par défaut dans dans l'espace cible alors on retient ce type
  • Sinon si le loggedMember peut publier le type FileDocument par défaut dans dans l'espace cible alors on retient ce type
  • Sinon on recherche le premier sous-type de document que le loggedMember peut publier dans l'espace cible

1.3.2 Modes de dépôt concernées

Les modes de dépôts concernés par la sélection automatique du type de documents sont :

  • Modal de dépôt de document (docChooserModal.jsp)
  • Zone de dépôt de l'explorateur de média
  • Zone de dépôt de la portlet Explorateur (docChooserInline.jsp)
  • Champ document des types de contenus (champ de type file)

1.3.3 Modes de dépôt non concernés

  • JDrive :
    • Création à partir d'un document : il y a déjà un algorithme de résolution basé sur le type par défaut défini dans les propriété de JDrive
    • Dépôt transparent : c'est le type de document défini dans les propriétés de JDrive qui est utilisé
    • Dépôt interactif : c'est l'utilisateur qui choisi le type de document
  • Add-in Outlook
    • Dépôt des pièces jointe
  • JCapture
    • JCapture dispose déjà d'une mécanique de choix du type selon le modèle de capture
  • Import de document
    • Les règles d'import permettent déjà de définir le type de document à utiliser selon la source d'import
  • Dépôt par le docChooser.jsp
    • Dans JPlatform 10, ce mode de dépôt n'est plus disponible pour les premiers dépôts. Il ne sert que pour le dépôt d'une nouvelle version (et dans ce cas, le type de document ne change pas).
  • Google Drive
    • Lors de la récupération d'un document
  • Office 365
    • Lors de la récupération d'un document
  • CMIS (client)
    • Lors de la récupération d'un document
  • Momindum
    • Lors de la récupération d'une vidéo du Cloud Momindum.

1.3.4 Propriétés de mapping Content-Type/Classe

Le mapping d'un type de fichier avec un type de document de JPlatform se fait par une déclaration de propriété. Ces propriétés commencent par le préfix file-document.upload.class et sont de la forme :

file-document.upload.class.<top-level/subtype>: <classe du type de document>=

<top-level/subtype> représente le MIME type. Il peut s'agir d'un MIME type complet (eg. image/jpegvideo/mp4application/pdf, ...) ou uniquement du top-level (eg. imagevideoapplication, ...)

Exemples :

file-document.upload.class.image: generated.Media
file-document.upload.class.video: generated.Media
file-document.upload.class.audio: generated.Media
file-document.upload.class.application/pdf: generated.MyCustomDocumentType

1.3.5 Propriété du type de document par défaut

Lorsque l'utilisateur dispose de plusieurs type de documents pour son dépôt et qu'il n'existe pas de mapping pour le contentType du fichier déposé, on se rabat sur le type de document par défaut à utiliser.

La définition du type de document à utiliser par défaut sur la plateforme se fait avec la propriété file-document.upload.class.default

Exemple :

file-document.upload.class.default: com.jalios.jcms.DBFileDocument

1.3.6 Type Media

Le type Media dérive de DBFileDocument. Il dispose des champs supplémentaire suivans :

  • mediaWidth
  • mediaHeight
  • mediaDuration

Il est à noter que, bien que destiné au stockage des médias, rien n'empêche un contributeur d'utiliser le type Media pour d'autres types de documents.

1.3.7 Interface de dépôt

L'interface de dépôt avancé ("Plus d'options") a été légèrement revue :

  • L'onglet "Méta-données" a été renommé "Informations"
  • Le choix du Workspace a été déplacé dans l'onglet Avancé
  • Le sélecteur de type est par défaut sur "Sélection automatique du type" si il y a plus que d'un type proposé
  • L'onglet Workflow n'apparait que si l'on choisi explicitement un type de document
  • Le lien amenant vers l'ancienne interface de dépôt (docChooser.jsp) n'est plus proposé

1.3.8 API

L'API FileDocument proposent de nouvelles méthodes statiques :

  • getUploadClass(String contentType) renvoie la classe de dépôt à utiliser pour le contentType donné ou null.
  • getDefaultUploadClass() renvoie la classe de dépôt à utiliser par défaut
  • getFirstDocumentClassAuthorized(Member, Workspace) renvoie la première classe de FileDocument que le membre donné peut utiliser dans le workspace donné. Revient à appeler la méthode suivante avec un contentType =null=
  • getFirstDocumentClassAuthorized(Member, Workspace, String contentType) renvoie la première classe de FileDocument que le membre donné peut utiliser dans le workspace donné, en commençant par la classe mappée dans les propriétés pour le contentType donné. (Ex: si le contentType est "image" ou "image/png", on essaye en premier le type generated.Media).

1.4 Structure de l'organisation et responsables de département / services / employé

De plus en plus de modules participent à la gestion des processus de l'organisation (JLearn, JProcess, Gestion des congés, Recrutement, ...)

Pour cela, ces modules ont besoins de connaitre la structure de l'organisation et la relation hiérarchique entre les membres (qui est responsable de qui). Il est notamment important de pouvoir déterminer les membres qui composent l'équipe d'un responsable.

Jusqu'à présent ces informations étaient portées par le module Organigramme (FlowChart). En conséquence plusieurs modules était donc dépendant de ce module pour pouvoir fonctionner.

JPlatform 10 lève cette contrainte en ré-introduisant dans le coeur une API de gestion de la structure de l'organisation.

L'article JPlatform 10 - API de gestion de l'organisation et des responsables de département présente en détail cette nouvelle API.

1.5 ReplicaMessage

Dans un cluster JSync, seules les données du store (et éventuellement les fichiers associés) sont recopiés sur les différents réplicas du cluster. JcmsDB étant commune aux différents réplica, les données sont accessibles par tous les réplicas.

JPlatform 10 introduit une nouvelle API, les ReplicaMessage, qui permet aux réplicas de communiquer en dehors des échanges JSync. Le protocole utilisé garanti la distribution des messages dans l'ordre d'émission, même si un réplica n'est pas joignable lors de l'envoi.

JPlatform 10 utilise cette API pour propager les changements sur la structure des Workflows entre les réplicas.

Cette API pourrait aussi être utilisée plus tard pour propager les changement sur certaines propriétés de paramétrage.

L'article JPlatform 10 - L'API des ReplicaMessage présente cette API en détail.

1.6 Partage de contenu entre espace

JPlatform 10 permet de partager un contenu entre plusieurs espaces. Le guide JPlatform 10 - Partage de contenu entre espaces illustre cette nouvelle fonctionnalité.

1.6.1 Modèle de données

Le partage de contenu entre espace est matérialisé par le nouveaux champ attachWorkspaceSet sur la classe Publication. Pour les contenus stockés en base, un deuxième champ, hasAttachWorkspaceSet, est stocké pour dénormaliser cette information et accélérer les requêtes.

class Publication {
  Set<Workspace> attachWorkspaceSet;
  boolean public boolean hasAttachWorkspaceSet() {...}
}

Ces évolutions sur le modèle sont transparentes pour les données existantes

1.6.2 Choix du portail d'affichage d'un contenu partagé

Il y a plusieurs modalité d'affichage d'un contenu :

  • Soit depuis un lien interne à l'application (PQF, résultat de recherche, historique de navigation, activité)
    • Soit depuis l'espace d'origine
    • Soit depuis un espace de rattachement
    • Soit depuis un autre espace
  • Soit depuis un autre site (depuis un lien externes : mail, url friendly (qr code, ...))

L'affichage d'un contenu dépend donc de 2 paramètres : l'endroit d'où le contenu est accédé et qui y accède.

Soit trois espaces E1E2 et E3

Soit C un contenu public de E1 qui est partagé dans E2 et E3.

Soit M1 un membre appartenant à E1E2 et E3

Soit M2 un membre appartenant à E2 mais pas à E1 et E3

Soit M3 un membre appartenant à E3 mais pas à E1 et E2

Soit M2-3 un membre appartenant à E2 et E3 mais pas à E1

Cette matrice indique dans quel espace doit être affiché C en fonction de qui fait l'accès et du lien avec lequel il est accédé :

  Lien dans E1 Lien dans E2 Lien dans E3 Lien dans un autre espace Lien externe
M1 E1 E2 E3 E1 E1
M2 - E2 - E2 E2
M3 - - E3 E3 E3
M2-3 - E2 E3 E2 ou E3 E2 ou E3

1.6.3 Modalité de rattachement / détachement de contenu

Soit C un contenu appartenant à l'espace E1

Le contenu C peut être partagé vers l'espace E2 par le membre M si la condition suivante est vrai : M est admin central OU (M a accès à C ET M est contributeur dans E2)

Dès qu'un partage a lieu, une alerte est envoyée à l'auteur de C et à l'ensemble des admin de l'espace de C indiquant qui a déclenché le partage (M) et dans quel espace (E2)

Un membre M peut arrêter le partage du contenu C dans l'espace E2 si la condition suivante est vraie : M est admin central OU M est l'auteur de C OU M est admin de E1 OU M est admin de E2 OU M est contributeur dans E2

1.6.4 Désactivation

La propriété publication.attach-ws.enabled permet de désactiver complètement cette nouvelle fonctionnalité.

Il n'est alors plus possible de rattacher/détacher des publications à des espaces. Si des publications avaient préalablement été attachées, elles n'apparaitront plus dans les espaces de rattachement.

1.6.5 API

Classe Publication

Méthode Description
canBeAttachedBy(Member) Retourne true si ce membre peut rattacher cette publication à un autre espace.
canBeAttachedTo(Workspace, Member) Retourne true si ce membre peut rattacher cette publication à cet espace.
canBeDetachFrom(Workspace, Member) Retourne true si ce membre peut détacher cette publication de cet espace.
getAttachedWorkspaceSet() Retourne l'ensemble des espaces auxquels est rattaché cette publication.
attachTo(Workspace, Set<Category>, Member mbr) Effectue le rattache de la publication dans l'espace donné avec les catégories données et pour le membre donné
detachFrom(Workspace) Effectue le détacment de la publication de l'espace donné.

Classe Channel

Méthode Description
isWSAttachmentEnabled() Retourne true si le rattachement est autorisé.

Classe PublicationCriteria

Méthode Description
setSearchInAttachWS(boolean) Permet d'indiquer si la recherche doit porter dans les espaces de rattachement.

1.7 Export JSON

Les API REST de JPlatform peuvent maintenant exporter leurs données au format JSON.

Pour utiliser cette fonctionnalité, il faut envoyer la requête REST avec un header Accept:application/json.

Ex.

curl --header "Accept:application/json" https://<jplatform>/rest/WhoAmI

Exemple de retour :

{
    "class": "com.jalios.jcms.Member",
    "id": "jn1_92854",
    "cdate": "2010-04-30T07:10:01Z",

...
    "login": "xxxxx",
    "name": "xxxxx",
    "firstName": "xxxx",
    "organization": "Jalios",
    "department": "R & D",
    "jobTitle": "Ingénieur R&D",
    "email": "xxxxxxxxxx@jalios.com",
    "mobile": "XX XX XX XX XX",

...

    "declaredGroups": [
        {
            "class": "com.jalios.jcms.Group",
            "id": "jn1_339640",
            "name": "xxxxxx"
        },
        {
            "class": "com.jalios.jcms.Group",
            "id": "jn1_103636",
            "name": "XXXXX"
        }
    ],
    "ldapSync": true,
    "lastLdapSynchro": "2017-10-09T20:00:00Z",
    "language": "en"
}

La fonctionnalité des relateds (décrite dans la documentation Services Web RESTful avec JCMS Open API) est aussi applicable dans ce cas.

Par défaut la librairie d'export json exporte tout le contenu d'un objet et aussi le contenu des champs de l'objet. On peut donc avoir une quantité importante d'information voir une boucle infinie (exemple d'un membre appartenant à un groupe qu'il a créé). Un autre paramètre a donc été ajouté pour éviter ce problème, c'est la notion de profondeur (paramètre depth). L'export se fait en respectant la notion de profondeur n (par défaut 1) et affiche un résumé des éléments de profondeur n+1. Si cette donnée est une data, on affiche la class, l'id et le titre.

Note : Cela ne change pas le format de soumission des données (toujours au format form data)

1.8 Autres nouveautés

1.8.1 DataSelector extends Predicate

Dans JPlatform 10 on peut donc utiliser tous les DataSelector comme des prédicats Java 8.

L'interface DataSelector dérive maintenant de Predicate<Data>. La méthode héritée test() a été implémentée avec une default method qui appelle la méthode isSelected()

Exemple : Récupération de l'ensemble des publications dont l'auteur est mbr et dont la date de publication est comprise entre startDate et endDate.

Set<Data> set = channel.getDataSet(Publication.class)
    .stream()
    .filter(
        Data.getAuthorSelector(mbr)
        .and(Publication.getPdateSelector(startDate, endDate))
    )
    .collect(Collectors.toSet());

1.8.2 Alert : attribut résumé (Summary)

Afin de garantir un affichage court dans le menu des alertes de la topbar, les alertes dispose d'un attribut summary. Cet attribut est optionnel et sera dans la plupart des cas vide.

Pour les alertes ayant une description avec beaucoup de texte il est recommandé de produire un résumé de quelques lignes.

Les résumé doivent être en HTML.

1.8.3 Alert : afficher l'abstract de la publication

Il est possible de demander à afficher le résumé de la publication lorsqu'une alerte est envoyée via certaines propriétés.

Par exemple pour afficher l'abstract de la publication mentionnée lorsqu'une alerte de nouvelle mention est envoyée, il faut ajouter :

alert.channel.web.content.abstract.mention.notification: true // Pour les alertes web
alert.channel.mail.content.abstract.mention.notification: true // Pour les alertes web

Ces deux propriétés correspondent à la déclaration d'alerte alert.name.mention.notification

1.8.4 EditPublicationHandler : setSkipActivity()

Il est possible de forcer la non prise en compte dans l'activité d'une écriture à partir d'un formHandler en positionnant l'attribut skipActivity sur le formHandler. Ceci est notamment mis en oeuvre lors des dépôts depuis le MediaBrowser.

1.8.5 DataController.CTXT_IS_SILENT_WRITE

L'entrée de contexte DataController.CTXT_IS_SILENT_WRITE permet d'indiquer qu'il s'agit d'une écriture silencieuse. Elle est alors ignorée des PublicationFollower et l'activité ESN.

1.8.6 WebPageMetaDataExtractor

Cette classe permet de récupérer les méta données d'un site distant en inspectant le code source (Les balises meta "og" sont privilégiées si disponibles).

Vous pouvez l'utiliser via le code suivant :

  1. WebPageMetaData metaData = WebPageMetaDataExtractorUtils.getWebPageMetaData(url, userAgent)  
  • url : l'url du site que vous souhaitez requêter
  • userAgent : l'userAgent de l'utilisateur actuel, ou, si vous ne le fournissez pas, un userAgent par défaut sera utilisé

L'objet WebPageMetaData contient :

  • Le titre de la page : getTitle()
  • La description de la page : getDescription()
  • L'url de la page (On récupère l'url canonical si elle est exposée via une metadata dans la source du site) : getUrl()
  • La main image (L'image mis en avant par la page courante) : getMainImage()
  • Les autres images (On privilégie les images des balises <article> et <main> du code source) : getImagesUrl()

Javadoc :

2. Outils

2.1 Générateur de types

Le générateur de type produit désormais des classes Java supportant l'évolution de schéma avec des type primitif sur les publications stockées en base.

Ainsi, si on ajoute un champ entier ou booléen sur un type de publication stocké en base, les anciennes valeurs seront automatiquement rechargées (et réenregistrés) avec la valeur par défaut du champ.

Il n'est donc plus nécessaire de passer des requêtes SQL pour les montées de version lorsqu'un champ primitif a été ajoutée à une type stocké en base.

2.2 Mise à jour des JAR

Une rationalisation des JAR utilisés par la plateforme et ses modules a été effectuée (normalisation des versions de librairies, résolution des conflits entre modules, ...).

Une procédure automatique de recherche de vulnérabilité sur les JAR embarqués par JPlatform (et ses modules) a été mise en oeuvre et de nombreux jars ont été mis à jour avec les versions plus récentes non vulnérables.

2.3 POM

Pour permettre des mises à jour plus fréquentes des librairies Java externes (jar) et garantir une parfaite cohérence de ces librairies entre le coeur et tous les modules, Maven a été mis en oeuvre dans le système de build de JPlatform 10 (actuellement uniquement pour la gestion des dépendances entre le coeur et les modules).
Pour cela, un pom parent commun au coeur et à tous les modules a été créé, c'est lui qui assure la cohérence de version de toutes les librairies.
Une version publique de ce fichier pom.xml est mis à disposition des intégrateurs et clients afin qu'ils puissent proposer des modules garantissant leur bonne intégration dans JPlatform (et ses modules) : JPlatform 10.0.0 - pom.xml

2.4 Lucene 6

La librairie Lucene a été mise à jour vers la version 6.4.2.

Des configurations simplifiées et nouveaux hook pour développements spécifiques sont disponibles.

2.4.1 Configuration

Les moteurs de recherche lucene des Publications, des Membres, ou des Catégories, peuvent désormais être personnalisés par simple configuration.

  • Des fichiers XML permettent de paramétrer le comportement d'indexation et de recherche de lucene.
  • Des propriétés JCMS exposent certains paramétrages spécifiques à l'intégration de lucene dans JPlatform

Configuration XML

L'initialisation de chaque moteur de recherche se fait à partir du premier fichier XML valide identifié, en privilégiant dans l'ordre :

  1. WEB-INF/data/lucene-{Publication|Member|Category}.xml : 
    c'est ce fichier que vous créerez pour personnaliser la configuration
  2. Puis les configurations standard fourni par Jalios (à ne pas modifier)
    1. WEB-INF/jalios/lucene/lucene-{Publication|Member|Category}.xml
    2. WEB-INF/jalios/lucene/lucene.xml

Le format de ce fichier XML est proche de celui du fichier schema.xml de Apache SOLR, sans toutefois proposer les mêmes fonctionnalités.
Il permet de spécifier les classes lucene à utiliser et leur configuration :

  • (requis) un Analyzer pour les traitements d'analyse lors de l'indexation et de la recherche, comprenant :
    • zero ou plus CharFilter
    • un Tokenizer
    • zero ou plus Filter
  • (optionnel) la classe Similary à utiliser pour le comportement de calcul de score lucene

Pour cela, le fichier utilise le format suivant :

<?xml version="1.0" encoding="UTF-8" ?>
<lucene>
  <analyzer>
    <charFilter name="..." ... attribute1="value1" attribute2="value2"/>
    <tokenizer name="..." />
    <filter name="..." /> 
  </analyzer>
  <similarity class="..."/>
</lucene>

Les CharFilterTokenizer et Filter sont chargés grâce à la classe CustomAnalyzer

  • ils peuvent être spécifiés
    • par leur nom (SPI names), avec l'attribut name
      Quelques exemples (non exhaustifs) disponibles dans lucene :
      • CharFilter : htmlstrip, mapping, persian, patternreplace
      • Tokenizer : keyword, letter, lowercase, whitespace, edgengram, ngram, pathhierarchy, pattern, classic, standard, uax29urlemail, wikipedia
      • TokenFilters : apostrophe, decimaldigit, lowercase, stop, type, uppercase, englishminimalstem, englishpossessive, porterstem, frenchlightstem, frenchminimalstem, asciifolding, capitalization, codepointcount, daterecognizer, fingerprint, hyphenatedwords, keepword, keywordmarker, keywordrepeat, length, limittokencount, limittokenoffset, limittokenposition, removeduplicates, stemmeroverride, trim, truncate, worddelimiter, edgengram, ngram, patternreplace, patterncapturegroup, reversestring, shingle, snowballporter, classic, standard, synonym, elision, suggeststop
    • par le nom complet (FQDN) de la Factory permettant de les créer, avec l'attribut class,
  • les autres attributs XML sont utilisés comme paramètres des factory
  • les valeurs de chemins de fichiers spécifiés en attributs sont relatif au répertoire WEB-INF/ de la webapp

La Similarity à utiliser peut être renseignée en spécifiant le FQDN de la classe dans l'attribut class.

Consultez les fichiers WEB-INF/jalios/lucene/lucene.xml et WEB-INF/jalios/lucene/lucene-Member.xml fournis en standard pour 2 exemples de configuration.

Notez également l'existence de la classe LoggingFilterFactory pouvant être utilisée pour diagnostiquer le processus d'analyse en activant les logs de niveau TRACE sur la classe com.jalios.jcms.search.analysis.LoggingFilter (dans la configuration log4j).
Exemple :

<?xml version="1.0" encoding="UTF-8" ?>
<lucene>
  <analyzer>
    <tokenizer name="classic" maxTokenLength="255"/>

    <filter class="com.jalios.jcms.search.analysis.LoggingFilterFactory" prefix="in" />
    <filter name="lowercase" />
    <filter class="com.jalios.jcms.search.analysis.LoggingFilterFactory" prefix="  after lowercase" />
    <filter name="classic" />
    <filter class="com.jalios.jcms.search.analysis.LoggingFilterFactory" prefix="  after classic" />

  </analyzer>
  <similarity class="org.apache.lucene.search.similarities.BM25Similarity"/>
</lucene>

Propriétés de configuration

Des nouvelles propriétés permettent de configurer le nombre maximum de résultat récupérés par lucene à chaque requete :

query.lucene.mbr.max-results: 2000
query.lucene.cat.max-results: 100
query.lucene.pub.max-results: 100
query.lucene.archive.max-results: 100

2.4.2 Développements

Recherche

Une nouvelle méthode LuceneSearchEnginePolicyFilter#parseQuery(String, ParseOptions, Analyzer, Query) permet d'intervenir lors de chaque recherche utilisateur pour construire l'objet lucene Query.
Ce hook remplace custom.LuceneSearchEnginePolicy.parse(String, ParseOptions, Analyzer) et les méthodes associées.

En l'absence de développement spécifique, le traitement est assuré par la classe com.jalios.jcms.search.queryparser.DefaultQueryParser.parse(String, ParseOptions, Analyzer)

Migration

Les développements précédement effectués dans LuceneSearchEnginePolicy doivent être adaptés

  • Les personnalisations du processus d'analyse, généralement effectuées dans l'ancienne classe custom.LuceneSearchEnginePolicy.CustomAnalyzer pour personnaliser les Tokenizer et TokenFilter se font désormais par configuration XML comme évoqué ci-dessus
    Afin de conserver un comportement isofonctionnel par rapport à JCMS 9, les développements coeur ont été migrés de la façon suivante : ,
    • L'ancienne TokenFilter ISOLatin1AccentFilter a été remplacée par l'utilisation du MappingCharFilter avec le fichier texte WEB-INF/jalios/lucene/mapping-FoldToASCII.txt fourni en standard
    • L'ancienne TokenFilter ApostropheFilter a été remplacé par l'utilisation du ElisionFilter avec le fichier WEB-INF/jalios/lucene/french-articles.txt fourni en standard
    • Les classes DashFilter et UnderscoreFilter ont été remplacé par le développement d'un Tokenizer spécifique à Jcms : le JcmsTokenizer.
      Ce Tokenizer est une modification de l'implémentation standard du ClassicTokenizer lucene et de sa grammaire ClassicTokenizerImpl.jflex pour y ajouter des règles de découpage spécifique.
    • Le nouveau TokenFilter ContextualStopFilterFactory permet d'appliquer des StopFilter en utilisant la langue courante d'analyse (que ça soit pour l'indexation ou la recherche).
  • Les personnalisations du processus de recherche (précédemment dans LuceneSearchEnginePolicy.parse) doivent être migrés vers une LuceneSearchEnginePolicyFilter.parseQuery

2.5 Activation et désactivation de modules à chaud

2.5.1 Cycle de vie d'un module

Jusqu'à JCMS 9 les différents états d'un module étaient :

  • Déposé:
    • le zip du module est présent mais il n'a pas été déployé
  • Arrêté :
    • Le module est déployé
    • L'une de ces 2 conditions est vérifiée
      • Dans plugin.xml init="false"
      • Le module a généré des erreurs au démarrage
  • Démarré
    • Le module est déployé
    • Dans plugin.xml init="true"
    • Le module n'a pas généré d'erreurs au démarrage

Avec JPlatform 10 les états d'un module sont :

  • Déposé:
    • idem
  • Arrêté :
    • idem
  • Chargé
    • Le module est déployé
    • Dans plugin.xml init="true"
    • Le module n'a pas généré d'erreurs au démarrage
    • Et le module est désactivé
  • Actif

Idem chargé mais le module n'est pas désactivé

2.5.2 Propriété d'activation / désactivation de module

L'activation ou la désactivation de module est matérialisée par une propriété de la forme :

plugin.<PLUGIN_NAME>.enabled: true/false

Exemple :

plugin.JReadingPlugin.enabled: false
plugin.JTaskPlugin.enabled: true

Si la propriété est absente pour un module, le module est considéré comme activé.

2.5.3 Gestion des composants

Les composants représente l'ensemble des points de débranchement que peut fournir un module :

  • StoreListener
  • DBListener
  • DataController
  • ChannelListener
  • QueryFilter
  • CleanFilter
  • AuthenticationHandler
  • RightPolicyFilter
  • AlarmListener
  • JSyncListener

Par extension on inclus aussi dans les composants

  • Les resources Open API
  • Les templates

Ces composants sont branchés au démarrage de JPlatform pour tous les modules actifs.

Lorsqu'un module est activé à chaud, la méthode ChannelListener.initAfterStoreLoad() est appelé pour chacun de ses ChannelListeners.

Note : les resources Open API ne seront activées/désactivées à chaud qu'à partir de JPlatform 10 SP1 ou avec l'application du patch https://issues.jalios.com/browse/JCMS-6227

2.5.4 Gestion des propriétés du modules

Les propriétés d'un module ne sont chargés que si le module est actif à l'exception des propriétés de langues qui sont chargé même si le module est inactif.

A l'activation et à la désactivation d'un module on appelle donc, comme lors du démarrage, la méthode Channel.reloadProperties() qui rejoue l'ensemble des propriétés

2.5.5 Gestion des types

Jusqu'à JCMS 9, les types livrés par un module n'était pas différentié des types du coeur ou de l'application.

A partir de JPlatform 10, pour chaque type on peut connaitre le module qui l'a livré. L'information est accessible par la méthode TypeEntry.getPlugin().

Tous les types sont chargés ; même ceux des modules inactifs ou arrêtés. Cependant, seuls les types du coeur, de la webapp ou des modules actifs sont affichés dans les différentes interfaces.

2.5.6 Gestion des workflows

Jusqu'à JCMS 9, les workflows livrés par un module n'était pas différentié de ceux du coeur ou de l'application.

A partir de JPlatform 10, pour chaque workflow on peut connaitre le module qui l'a livré. L'information est accessible par la méthode Workflow.getPlugin().

Tous les workflows sont chargés ; même ceux des modules inactifs ou arrêtés. Cependant, seuls les workflows du coeur, de la webapp ou des modules actifs sont affichés dans les différentes interfaces.

2.5.7 Gestion des raccourcis (shortcut)

Les shortcuts sont des contenus qui sont créé au démarrage de JCMS par analyse des propriétés de modules.

La génération des shortcuts se basant sur les propriétés, si un module est inactifs, ses propriétés n'étant pas chargés, les éventuels shortcuts ne seront pas générés.

Lorsqu'on active un module à chaud, le ShortcutManager est appelé pour générés les éventuels shortcut associés.

Lorsqu'un module est désactivé, le ShortcutManager est appelé pour supprimer les éventuels shortcut associés.

2.5.8 Gestion des caches

Lors de l'activation ou la désactivation d'un module les données en cache peuvent devenir invalide. Aussi, à chaque activation/désactivation de module on supprime l'ensemble des entrées du CacheManager.

2.5.9 Gestion des dépendances

A partir de JPlatform 10 SP1, la gestion des dépendances est enrichi pour permettre d'avoir des dépendance sur des modules actifs (par défaut) ou désactivé. Pour chaque dépendance il est possible de préciser avec l'attribut active que l'on dépend d'un module mais sans qu'il soit forcement actif (active="false"). Si l'attribut n'est pas renseigné alors c'est comme si il était à true.

Exemple :

<dependencies>
  <dependency name="Plugin1" /> <!-- Same as active="true" -->
  <dependency name="Plugin2" active="true" />
  <dependency name="Plugin3" active="false" />
</dependencies>

2.5.10 Blocage des JSP des modules

Afin d'empêcher l'accès aux JSP d'un module désactivé, on contrôle :

  • L'accès aux URL (relatives) commençant par plugin/*.jsp
  • Les inclusions de JSP commençant par plugin/ via l'attribut jsp des requêtes

Le contrôle est réalisé par la ServletFilter PluginAccessFilter avec le mapping =/plugin/*=

Les JSP livré en dehors du répertoire plugin/NomDuPlugin/ ne sont pas contrôlées.

2.5.11 Limites

JSync
L'activation et la désactivation d'un module nécessite de mettre à jour une propriété. Par ailleurs le module durant son activation peu lui aussi générés des propriétés (eg virtual id d'un shortcut ou de données produites par un ChannelListener).

JSync n'offrant pas de support à la réplication des propriétés, si un module est activé sur un noeud il ne le serait pas sur les autres noeuds.

L'activation ou la désactivation d'un module sur un cluster JSync nécessite donc de l'activé sur le leader puis de recopier les propriétés générés sur l'ensemble des noeud du cluster.

ChannelListener::initBeforeStoreLoad()
Lors de l'activation d'un module à chaud la méthode ChannelListener::initBeforeStoreLoad() n'est pas appelée. Si elle doit être appelé alors il faut redémarrer JCMS une fois le module activé.

webapp-files
Si un module livre des webapp-files (p. ex. des patches) alors il ne seront pas désactivés

ServletFilter
L'activation/désactivation des modules ne prend pas en charge la déclaration des Servlet et ServletFilter. De même si un module est désactivé, cela ne désactive pas ses éventuelles ServletFilter. Le cas échéant, c'est au développeur de faire les contrôle d'activité du module dans les ServletFilter.

2.6 Authentification par certificat

Un nouveau mode d'authentification a été ajouté à JPlatform. Il s'agit de l'authentification par certificat SSL client.

Dans le cadre d'une communication SSL, si le client envoit un certificat valide, enfant direct d'une authorité référencé dans JPlatform et contenant le sujet adéquat, il sera alors authentifier sans avoir à fournir de mot de passe.

La description et l'installation de cette fonctionnalité est disponible dans la fiche Authentification par certificat client.

2.7 Compilateur LESS

Le compilateur LESS embarqué dans JPlatform 10 utilise une nouvelle librairie : less4j. Cette librairie gère des syntaxes LESS qui n'étaient pas supportées avec l'ancienne librairie (lesscss-engine).

Une nouvelle propriété a été ajoutée dans jcms.prop pour permettre la génération des fichiers sourcemap associés aux fichiers CSS générés : channel.less-compile.sourcemap. Pour activer la génération des fichiers sourcemap, il faut passer cette propriété à true.