4 nov. 2021 11:50:12 Thomas Dumont avatar

Architecture logicielle

Le choix général d'architecture repose sur la spécification Java EE qui apporte un cadre aux applications d'entreprise n-tiers.

Découpage général de l'architecture

Les choix de la conception détaillée se sont portés sur une architecture en quatre couches côté serveur :

  • La couche présentation assurant les fonctions de gestion de l'interface utilisateur.
  • La couche service assurant les fonctions d'interface entre la couche métier et la couche présentation, ainsi qu'un ensemble de services techniques : cache, transformations XML/XSL vers HTML ou HTML vers XHTML.
  • La couche métier assurant la gestion des objets modélisant l'activité métier.
  • La couche d'accès aux données qui assure la persistance des objets métiers.

Schéma de restitution du contenu

Le schéma suivant présente de manière simplifiée le processus de restitution d'un contenu Front Office. Le schema se lit de gauche à droite et représente les différentes étapes commençant à la requète HTTP du client et terminant à la réponse HTTP du serveur. Le schéma est suivi d'explications sur les différentes étapes, puis de descriptions détaillées des différentes couches.

architecture

Les différents composants sont representés par des symboles différents: ceux de la couche présentation par de simples rectangles, ceux de la couche service avec un engrenage, ceux de la couche métier et ceux de la couche d'accès au données par une feuille de papier. Les couleurs indiquent en blanc les composants de lutece-core, en bleu les composants du plugin-document, un plugin standard de lutèce, et en vert des composants qui pourraient être apportés par un nouveau plugin.
Ce schema donne donc plusieurs exemples d'architecture dont on peut s'inspirer lors de l'écriture de nouveaux plugins.

Les étapes de restitution

Comme vu précédemment, Portal.jsp est le point d'entrée de restitution.
Comme toutes les JSPs dans Lutece, Portal.jsp utilise une directive <jsp:useBean> pour charger un bean java (ici PortalJspBean), ce qui permet d'écrire le code dans une classe java normale afin de faciliter la maintenance. PortalJspBean implémente des méthodes correspondant à différentes opérations de l'utilisateurs, et délègue la plupart des fonctionnalités à des classes java de services : PortalService, ContentService, AppPropertiesService, etc.

La suite du traitement consiste à utiliser les paramètres HTTP pour sélectionner un Content Service. Les Content Services sont des classes java comprenant principalement une méthode isInvoked indiquant selon les paramètres HTTP si c'est ce service qui est demandé, et une methode getPage renvoyant le contenu HTML en utilisant un cache. Les Content Services proviennent du coeur de Lutece ou de plugins. Pour reprendre les exemples du précédent chapitre, voici différents Content Services :

  • PageContentService de lutece-core, paramètres HTTP "page_id=n". Ce Content Service gère les pages éditoriales du site. Ces pages sont configurées en back office en y organisant des portlets. Ces pages sont organisées en une arborescence du site Lutece reflétée dans les menus et le fil d'ariane.
  • XPageAppService de lutece-core, paramètres HTTP "page=name". Ce Content Service gère des pages autonomes appelée XPages fournissant des fonctionnalités spécifiques. Le contenu est entièrement fabriqué par la classe java appelée et n'est généralement pas aussi éditable que les pages du PageContentService avec leurs portlets.
  • DocumentContentSerivce de plugin-document, paramètres HTTP "document_id=n&porlet_id=m". Ce Content service est fourni par le plugin document qui propose dans Lutece des contenus typés (par exemple, article, brève, etc.) organisés autour d'un workflow de publication (gestion de la collaboration, de la validation).

Sur le schéma de restitution du contenu, dans la partie 3a), si une page du PageContentService est demandée, le bean s'adresse au PageService qui fournira la page si elle est dans son cache ou sinon il la construira. La construction d'une page fait appel à la classe PageHome pour obtenir une instance de la page demandée. Ensuite, le contenu de chaque rubrique de la page est récupéré en XML puis transformé à l'aide de la feuille de style XSL associée, et agrégé pour constituer la page. La nouvelle page construite est alors mise dans le cache. Ici, le point important d'architecture est que les class services s'appuient sur des classes d'objets métiers, par exemple PageHome et Page, qui chargent leur données depuis des objets DAO (Data Access Object), par exemple PageDAO.
Les parties 3b) et 3c) fonctionnent sur le même principe en s'appuyant sur des services techniques et des objets métiers différents.

Architecture logicielle par brique : description des couches logicielles

Couche de présentation

Cette couche est réalisée à l'aide de JSP, de Java Beans, éventuellement de servlets. Le code HTML est produit de deux manières :

  • Pour les éléments les plus dynamiques du portail (par exemple les documents du plugin-document), le HTML est produit par transformation du contenu XML à l'aide de feuilles de style XSL. Le contenu XML pourra éventuellement contenir lui-même du code HTML pour tout ce qui concerne les contenus non structurés et notamment les pages locales.
  • Pour les éléments qui ne requièrent pas le support de plusieurs formats de restitution (par exemple le module d'administration ou les XPages), des fichiers contenant des modèles de représentation HTML appelés Templates seront utilisés.

Les JSP

Pour rappel, nous parlons ici de la catégorie à laquelle appartient Portal.jsp, le point d'entrée principal de restitution de contenus du Front Office.

De manière générale, le rôle attribué aux JSP est strictement limité à la gestion de la structure des pages et à la navigation. La partie HTML sera construite par des beans qui utiliseront des templates pour construire le code qui sera inséré dans les pages JSP.

Les JSP contiendront donc essentiellement :

  • la directive <jsp:useBean> qui charge un bean contenant la majorité du code.
  • la directive de redirection sur la page d'erreur : <%@ page errorPage="path/to/ErrorPage.jsp"%>
  • des directives d'inclusion d'objets statiques :<jsp:include> (ex : code HTML pour inclure une feuille de style)
  • des branchements conditionnels (<jsp:forward>)

Tout code Java et HTML est proscrit dans ces pages.

Les JSP doivent toutes contenir une JSP d'entête et une JSP de fin de page. Ceci permet d'intégrer dans toutes les pages des traitements initiaux et finaux sans avoir à modifier l'ensemble des pages (ex : test du timeout de la session, entête et pied de page HTML, lien vers une feuille de style, ajout de traitements statistiques, ...).

Le Front Office n'utilise principalement que Portal.jsp, alors que le Back Office utilise de nombreuses JSP. Cela permet d'avoir plus d'homogénéité dans le Front Office et plus de flexibilité dans le Back Office.

Les JspBeans

Pour rappel, nous parlons ici de la catégorie à laquelle appartient PortalJspBean, le bean associé à la JSP Portal.jsp vue dans les exemples.

Ces beans sont chargés de gérer la présentation des informations issues des objets métier. Ils doivent notamment produire du code HTML qui sera inséré dans les JSP. Ces composants sont regroupés dans le package fr.paris.lutece.portal.web pour le coeur de lutece, fr.paris.lutece.plugins.*.web pour les plugins. et ont un suffixe JspBean pour les différencier des beans métier.

Les JSP beans peuvent être utilisés par plusieurs pages JSP traitant des mêmes informations, notamment pour partager des constantes.

La portée des JSP beans (attribut scope dans la déclaration du bean dans la JSP) doit être soit session s'il contient des variables d'instance relative à la session de l'utilisateur (état conversationnel), soit request s'il n'a pas de variable d'instance. Dans certains cas, les scopes page ou applications sont aussi utilisés.

Les templates

Les templates sont des fichiers .html contenant un template de code HTML et éventuellement Javascript. Le moteur de template utilisé est FreeMarker http://freemarker.org. Par exemple, le template principal associé à Portal.jsp est le fichier page_frameset.html.

Après le templating Freemarker, certaines transformations additionnelles sont effectuées dans cet ordre :

  • 1) Le traitement des libellés multilingue introduits sous la forme . La gestion de l'internationalisation (i18n) sera décrite plus loin dans un chapitre dédié.
  • 2) Le traitement des libellés sous la forme DS Value Missing dont les valeurs sont stockées en base de données (dans le datastore de Lutece).
  • 3) Les traitements d'éventuels ContentPostProcessor comme ceux des plugins plugin-extend ou plugin-seo.

L'avantage de ces templates est qu'ils peuvent être partagés par plusieurs JSP et qu'ils peuvent être modifiés par des concepteurs graphiques sans risque d'impact sur les traitements. Ces templates seront stockés dans le répertoire /WEB-INF/templates.

Les feuilles de style XSL

Les feuilles de style XSL servent à transformer les contenus XML en HTML. Par ce mécanisme, on sépare le contenu de la forme. Par simple changement de style les mêmes contenus peuvent être présentés de manière très différente.

Les services

Les services servent à implémenter des fonctionnalités qui ne sont pas directement reliées à la création et à la modification d'un objet métier et de ses données. C'est souvent la partie la plus importante en termes de répartition de fonctionnalités d'une application.

C'est dans cette catégorie que se trouvent les Content Services, qui implémentent le framework dans lequel la majorité des contenus du Front Office sont restitués: PageContentService et XPageAppService.

Un certain nombre de services techniques fournis par le coeur sont accessibles à partir des composants de l’application. Par exemple:

Service Description
AppTemplateService Renvoie un modèle HTML à partir de son nom. Ce service dispose d’un cache activable qui permet de ne pas relire sur disque un modèle qui a déjà été demandé.
AppConnectionService Fourniture de connexions à la base données à partir d’un pool. Ce service est paramétrable pour utiliser soit le pool du serveur d’application, soit un pool de connexions autonome.
AppPropertiesService Renvoie les valeurs des variables définies dans les fichiers properties des répertoires /WEB-INF/conf/ et /WEB-INF/conf/plugins/
MailService.java Permet l'envoi de mail en format text ou html, avec gestion d'évenements de calendrier.
I18nService.java Gère la traduction de chaines de caractères en fonction du choix de l'utilisateur
AppLogService.java Gère les logs avec différents niveaux de sévérité, différents loggers
XmlTransformerService.java Gère l'application de feuilles de styles XSL pour transformer des contenus XML.

Il ne s'agit pas ici de donner une liste exhaustive des différents services du coeur, car la nature des services fait que des fonctionnalités très variées sont y sont implémentées. Il est conseillé de parcourir le code du coeur et de plugins incontournables (par exemple plugin-document, plugin-directory) pour avoir des exemples de fonctionnalités à regrouper dans des classes de services.

La couche métier

La couche métier est réalisée par un ensemble de classes correspondant à des objets métiers.

Ces classes ne contiennent aucun code technique HTML ou SQL. Ces composants sont regroupés dans les packages fr.paris.lutece.portal.business.* pour le coeur et fr.paris.lutece.plugins.*.business.* pour les plugins.

La persistance de ces objets est assurée par des objets DAO (Data Access Object) dont l'interface contient les principales méthodes d'accès aux données correspondant aux opérations SQL de base : load (SELECT), store (UPDATE), create (INSERT), delete (DELETE).

Nous avons décidé d'avoir une conception proche des Enterprise JavaBeans, et nous utilisons une classe "Home" pour chaque objet métier principal, c'est à dire disposant d'un DAO, inspirée des EJB Home Interface. Les prérogatives de ces classes sont, comme pour les EJB, d'assurer la gestion des instances de l'objet métier :

  • Création d'une instance (méthode create() )
  • Renvoi d'une instance à partir de sa clé primaire (méthode findByPrimaryKey())
  • Renvoi d'une collection d'instances pour un critère donné (méthodes findByCritère())

À ces méthodes qui figurent dans l'interface des classes Home des EJB entity, nous ajouterons les méthodes qui normalement correspondent à des actions gérées par le conteneur d'EJB. L'objet Home étant le seul objet ayant accès au DAO, c'est lui qui assurera les opérations de modification et de suppression des entités dans la base de données.

Les appels à ces méthodes seront effectués par le biais de méthodes update() et remove() au niveau de l'objet métier.

Voici le tableau récapitulatif des interfaces classiques des différents objets. Une ligne represente des méthodes liées, leur portée, et l'existance de cette méthode dans le modèle de programmation des EJB entity :

Objet MétierHomeDAO
MéthodePortéeMéthodePortéeEJBMéthodePortéeEJB
_createpublicXinsertpackageX
_findByPrimaryKeypublicXloadpackageX
_findBy...publicXselectBypackageX
updatepublicupdatepackagestorepackageX
removepublicremovepackagedeletepackageX

Une recommandation importante de la conception d’EJB entity est d’avoir une granularité assez grosse, c'est à dire que tout objet métier ne doit pas nécessairement être implémenté sous la forme d’un EJB entity. Tous les objets métiers dépendant d’un objet métier principal, et particulièrement dans le cas où il est en lecture seule, doivent être implémentés sous la forme d’une classe simple. La lecture de cet objet sera alors réalisée au niveau du DAO de l’objet principal.

Base de données Lutèce

Les requêtes SQL écrites dans les fichiers DAO doivent être compatible avec la norme SQL92.

Les requêtes SQL et les types de colonnes dans les scripts de création de la base de données doivent être compatibles avec les bases de données MySQL ou MariaDB. Le système de Lutece de création de la base de données basé sur Ant assure une transcription pour les SGDB PostgreSQL, HSQLDB et Oracle.