Loading presentation...

Present Remotely

Send the link below via email or IM

Copy

Present to your audience

Start remote presentation

  • Invited audience members will follow you as you navigate and present
  • People invited to a presentation do not need a Prezi account
  • This link expires 10 minutes after you close the presentation
  • A maximum of 30 users can follow your presentation
  • Learn more about this feature in our knowledge base article

Do you really want to delete this prezi?

Neither you, nor the coeditors you shared it with will be able to recover it again.

DeleteCancel

Make your likes visible on Facebook?

Connect your Facebook account to Prezi and let your likes appear on your timeline.
You can change this under Settings & Account at any time.

No, thanks

Architecture J2EE

Après un historique des architectures du Mainframe au N-TIERS, le cours se focalise sur les bases de J2EE avant de présenter des bonnes pratiques (logs, exceptions, environnements) et une implémentation avec Spring (IOC, AOP et MVC)
by

Pierre Le Roux

on 30 January 2014

Comments (0)

Please log in to add your comment.

Report abuse

Transcript of Architecture J2EE

Architecture N-TIERS J2EE
Pierre Le Roux
Responsable informatique Média Courtage
Diplômé de l'UBO en 2004

Chef de projet chez Capgemini pendant 7 ans
Clients : Ifremer, Orange, SFR et Arkéa

Spécialisé en architectures J2EE et Système d'Information

Moi
fr.linkedin.com/in/pilerou/
pilerou@gmail.com
1 système centralisé
Des consoles
Mainframe
Langages :
COBOL
AS400
Quand le mainframe a-t-il disparu ?
IL n'a pas disparu ! Et il n'est pas prêt de disparaître : Banques, Telecom, grande distribution (FNAC...)
Il est conservé pour sa robustesse et pour le poids qu'il a dans les Systèmes d'Information
Programme installé sur tous les postes
Export de données pour :
intégration dans le mainframe
impression papier... puis ressaisie manuelle
Poste dédié
Client lourd relié
au serveur
L' évolution des architectures informatique
Architecture web
Années 90 - Début 2000
Rôle du client limité à l'interprétation du HTML
Technologies de présentation peu poussées
Application cliente installée sur chaque poste
Robustesse de l'architecture
Déploiement de mise à jour compliqué
Peu de modularité
Capacité d'évolution moyenne sur le long terme
Cloisonnement des couches métier et présentation souvent négligé ==> nécessité de dupliquer/adapter tout le code pour déployer sur un nouveau canal
Architecture encore adaptée
aux petites structures (peu de développeurs)
aux start-ups (un brouillon avant la Cathédrale)
Architecture NTIERS
Depuis 2003
Variété de navigateurs Internet plus large à gérer et impacts majeurs selon les versions (IE6, 7 et 8)
Capacités graphiques plus poussées
Bande passante plus large (ADSL) pour des contenus plus riches (Flashs, design soigné, flux de données atteignant plus d'1 Mo pour certaines pages...)
La bonne pratique reste de limiter l'intervention du navigateur à l'interprétation du HTML et CSS et d'éviter l'implémentation de règles côté client (contrôle Javascript, logique métier côté client, règles de traitement Javascript...)
Début d'Internet sur mobile avec le WAP et l'imode
Connectivité très limitée ==> Flux très léger et présentation graphique minimaliste
Priorité donnée à l'accès à l'information
Forte disparité des rendus selon les terminaux
Interprétation de code WML uniquement et styles basiques
Javascript possible sur certains terminaux mais clairement à proscrire sur les technologies WAP et Imode
Intégration directe de partenaires externes via des standards de communication transactionnelle : SOAP, HTTP
Echange de flux XML
Interrogation à la demande du client final
Facilitation du développement des plateformes B2B avant le Cloud
Réseau performant entre les partenaires (fibre, ligne dédiée...)
Dessert des ressources statiques web (images, css...)
Reçoit des requêtes HTTP et renvoie un flux HTML (dans la plupart des cas)
Les flux HTML sont généralement construits de façon dynamique
Les données sont obtenues en appelant un service métier puis sont transformées pour être envoyées au client
Conserve un état de la navigation des utilisateurs (Statefull)
Même fonctionnement que les sites web destinés aux ordinateurs de bureau
Pages et ressources plus légères
Traite des requêtes SOAP
XML suivant une norme non liée à la technologie : .net, J2EE, php...
transmis par appels HTTP
Obtient les informations en appelant les services métiers
Ne conserve aucun état d'une requête à une autre (Stateless)
Répond en envoyant un flux XML suivant une norme
Objectif : normer les échanges et faciliter l'intégration par des partenaires
Variété plus large de frameworks
Problèmatiques différentes des Webservices mis à disposition des partenaires :
Optimisation du temps de traitement unitaire d'un appel
Sécurité souvent moins accentuée
Intégration uniquement avec des clients internes
Effectue l'ensemble des traitements métier
Coordonne des traitements et des appels aux sources de données
Garantie la cohérence des données
Fournit une réponse indiquant le statut de traitement
Cloisonnement par domaine fonctionnel dans la plupart des groupes
Enjeux :
Eviter les duplications de données en définissant les responsabilités de chaque sous-système
Garantir la cohérence des données entre les systèmes
Éviter le "plat de spaghettis"
Base de données transactionnelle
Peut être découpée en plusieurs Base de données
Maintenir la modularité services métier dans la base de données
==> Un module métier dont la vocation est de gérer les données "personnes" ne doit pas accéder aux informations de "contrats"...
Couche métier historique des grands groupes : nécessité de conjuguer avec cette technologie
Part parfois très importante des traitements métiers
Capacité à appeler le mainframe comme un webservice
Traitements métiers migrés progressivement vers les couches métiers
Interactions potentielles avec des services externes pour obtenir des informations de façon transactionnelle
Master 2 DOSI
Développement de l'Offshore
des Systèmes d'Information
Métier
Architecture en couches applicatives
Accès aux données :
Requête Base de données
Lecture de fichiers
Lecture / écriture de données

DAO
DAO : Data Acces Object

Bases de données SQL
JDBC : librairie native JAVA d''accès aux bases de données par requête SQL
JPA / Hibernate / Ibatis... : Couche d''abstraction des données au dessus de JDBC
ORM : Object Relationship Management : Structuration de la base de données en objet
Pas ou peu de SQL pour exécuter les requêtes
Facilités à changer de base de données
Technologies DAO J2EE
Autres technologies NO SQL (Not Only SQL)
Mongodb, Cassandra...
Stockage des données en base sous forme d''objets structurés au lieu d''un stockage d''enregistrements "à plat"
Gestion de fichiers ou de flux
Lecture/écriture de fichiers
Consommation de fichiers présents sur un autre serveur via des protocoles de transport : FTP, HTTP, SCP, CFT...
personne
idpersonne
nom
prenom
date_naissance
adresse
idadresse
rue
code_postal
ville
personne_id
telephone
idtelephone
numero
personne_id
Personne
idpersonne : 12345,
nom : "Toto",
prenom : "Tata",
dateNaissance : "01/04/1999",
telephones : ["0203040506", "0602030405"],
adresses : [
{ rue : "dsqs", codePostal : 29200, ville : "Brest"},
{ rue : "qdf", codePostal : 44000, ville : "Nantes"}
]
Déclaration des requêtes
Implémentation des fonctions
Cas d'utilisation :
Insérer une personne et les adresses et téléphones renseignés :

Insérer une personne et les adresses et téléphones renseignés :
Alimentation de l'objet personne
Insertion de l'objet personne

==> 1 seule requête. Pas de besoin de gestion de transaction

Pour aller plus loin : formation gratuite en ligne sur https://education.mongodb.com/

Cas d'utilisation avec Mongodb :
Client
Controlleur
Appel à des services distants :
Utilisation d'un protocole défini avec le service distant : REST, SOAP...
Utilisation d'une API pour faciliter l'intégration :
SOAP : JAX-WS, CXF, Spring-WS...
REST : JAX-RS, Spring, CXF...

Enjeux d'une couche d'accès aux données
Choisir la source de données :


Base de données relationnelle
Population d'utilisateurs large et capacité à trouver facilement des développeurs et concepteurs sur ces technologies
Technologies et méthodes de conception éprouvées
Robustesse de l'intégrité des données (transactions...)
Base de données NO-SQL
Technologies désormais matures pouvant être déployées dans les grandes structures
Technologies libres supportées par des grandes entreprises: Hadoop (Apache), Mongodb (10 gen)...
Performance et capacité à gérer des gros volumes de données (BIG DATA)
Beaucoup de requêtes pour chaque demande d'utilisateurs ==> Performances perfectibles
Maintenance des applications plus complexe et développement plus lourds nécessaires dans les couches d'accès aux données afin de gérer la multitude de tables

Exemple : la base de données de AcommeAssure contient 120 tables.
Si la base de données était refondue en MongoDB, elle contiendrait 15 collections.
Scalabilité variable : capacité à gérer de gros volumes de données et à tenir des temps de réponse acceptable

Stockage dans un mode structuré déjà intuitif pour l'exploitation par des applications. Pas de nécessité d'outils ORM (Object Relationship Management)
Pas ou peu de gestion des transactions. Risque sur l'intégrité des données
Nécessité moins avérée car plusieurs actions sur une collection (équivalent à une table) se fait en une seule requête


Mode de requêtage intuitif sous forme de requêtes non SQL.
Vigilance sur les compétences des développeurs.
Choisir la technologie d'accès aux données


Requête native SQL en JDBC
Facilité à tester les requêtes telles qu'elles vont être exécutées par le serveur de Base de Données ==> Capacité à optimiser les requêtes.
Nécessité de gérer par plusieurs requêtes manuelles et longueur de code
Lourdeur éventuelle de la maintenance lors de la modification d'un champ...

Meilleure identification de la liste des requêtes qui seront exécutées sur chaque demande d'un client
Utilisation d'un ORM
Capacité à modéliser sous forme d'objets une base de données et à donner une couche d'abstraction plus intuitive pour la modification
Capacité à changer de base de données car l'ORM est capable de se connecter à Mysql, Oracle... indifféremment
Conception de la couche d'abstraction souvent effectuée en maîtrisant mal les requêtes qui seront effectivement exécutées sur le serveur. Problèmes latents souvent masqués sur les plateformes de développement (faible volume de données) mais rencontrés sur les plateformes destinés aux clients :
Chargement de données trop massif
Profondeur de la récupération des données mal maîtrisé
Nécessité de confier les tâches dans les DAO à des développeurs qui ont une vision claire des impacts engendrés par des modifications les modèles
Synthèse
Pas de choix idéal collant à tous les contexts
Choix à effectuer en fonction :
du volume de données traité
de l'importance accordée à l'intégrité des données
des compétences des équipes de développement
Ouverture de transaction
Appel de "ajouterPersonne" ==> Récupérer l'identifiant de la personne généré
Pour chaque téléphone saisi :
Appel de "ajouterTelephone" en passant l'identifiant de personne obtenu en retour de "ajouterPersonne"
Pour chaque adresse saisie :
Appel de "ajouterAdresse" en passant l'identifiant de personne
Fermeture de transaction

==> Au moins 5 requêtes SQL exécutées... Plus dès que le modèle de gestion des personnes est plus compliqué (plus de tables)
But de la couche métier
Coordonner les traitements des couches DAO et client
Transforme les informations émanant du contrôleur pour les transmettre à la DAO
Pas d'appel directement aux sources de données : délégation à des DAO
Généralement garant des transactions pour l'intégrité des données

But du contrôleur
Couches techniques
Fournir des services techniques aux différentes couches afin de
faciliter les échanges entre les couches
améliorer la modularité
Généraliser certaines actions à toute l'architecture logicielle
structurer les développements en facilitant l'utilisation de design patterns
gestion des environnements
Assurer l'interface avec le client en mettant à disposition une structure d'échange normalisée
Transformer les informations transmises par le client pour communiquer avec la couche métier
Utilise généralement une brique technique pour interpréter la demande du client et pour transformer la structure de données à retourner en vue dans le format attendu par le client (JSON, HTML, XML... mais aussi PDF...)

Servlet
POST /action/creer-personne
web.xml
com.mycompany.myapp.ActionServlet
Classe implémentant l'interface javax.servlet.Servlet
et héritant (souvent) la classe javax.servlet.http.HttpServlet
http://docs.oracle.com/javaee/6/api/javax/servlet/Servlet.html
void init() throws ServletException
Méthode appelée par le conteneur de Servlet (serveur d'applications):
au lancement de l'application si la servlet est déclarée avec une balise <load-on-startup> dans le web.xml
au premier appel nécessitant l'utilisation de la servlet (par exemple un appel http sur /action/creer-personne.
Méthode appelée avant toute prise en charge de requêtes Http
Méthode utilisée pour :
charger des informations contextuelles qui seront utilisées à chaque appel : fichier de configuration, listes statiques communes à tous les utilisateurs, mapping d'urls...
initialisation des couches métier exploitées par la servlet

void service(ServletRequest req, ServletResponse res)
throws ServletException, java.io.IOException
Méthode appelée à chaque réception d'une requête éligible au traitement par la servlet (selon la configuration du web.xml)
Rôle : analyser la requête, effectuer un traitement en le déléguant au service métier et écrivant dans l'objet réponse les informations attendues par le client
Dans le cas d'une classe héritant de HttpServlet, il est possible d'utiliser doGet, doPost... pour traiter les différents types d'appels HTTP.
PrintWriter out = res.getWriter();
String paramNom = req.getParameter("nom")
String id = serviceMetier.creerPersonne( paramNom );
out.println("Bonjour " + paramNom + "<br/>");
out.println("Votre idenitifiant est :" + id);
Récupération du paramètre de requête "name"
Appel du service métier et récupération de l'identifiant
Écriture dans le flux de réponse (OutputStream)
Service Metier
com.mycompany.myapp.ServiceMetier
Classe spécifique à votre système visant à orchestrer les traitements DAO, appels client et autres règles métier
String creerPersonne (String nom)

String id= serviceDelegateIdentifiant.getIdentifiantUnique()
referentielPersonneDao.insertPersonne(id, nom);
crmClient.notifierCreationPersonne(id, nom);

return id;
Cas d'un appel HTTP dans une architecture J2EE simple
Descripteur d'une application web dynamique J2EE
IOC (Inversion Of Control)
class A {







}
protected
B

b
;

public
A
() {

b
= new
B
();
}
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
com.mycompany.myapp.ActionServlet
</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>comingsoon</servlet-name>
<url-pattern>
/action/*
</url-pattern>
</servlet-mapping>
</web-app>
class B {







}
protected
C c
;

public
B
() {

c
= new
C
();
}
Cas d'interaction entre classes sans Injection de dépendance
Instanciation
par un programme
Classe main, servlet...
Lancement des appels
aux fonctions de traitements
X fois...
Instanciation des
objets uniquement
Avec injection de dépendances
class AImpl {
}
protected
B

b
;

public
AImpl
() {
}
public
setB
( B
b
) {
this.
b
=
b
;
}
interface B {
}

public int delegateDoSomething (
int parameter...);
interface BImpl implements B {
}
protected
C c
;

public
setC
(
C c
) {
this.
c
=
c
;
}
Couche technique d'injection des dépendances
// Instanciation de singletons
C c
= new
CImpl()
;
B b
= new
BImpl()
;
A a
= new
AImpl()
;
Injection de la dépendance

une instance de Bimpl
Exploitation de l'implémentation de
B
sans savoir de quelle classe il s'agit
AOP (Aspect Oriented Programming)
Gestion des logs
Gestion des environnements
Intérêt de l'IOC et des frameworks associés
Focaliser le développeur sur l'interface et non l'implémentation d'une couche
Rendre possible le changement d'implémentation de façon transparente pour l'utilisateur (bouchon, nouvelle implémentation car changement de base de données...)
Exploiter la puissance de frameworks tels que Spring ou Guice pour faciliter les injections et les déclarations
Favoriser et faciliter l'utilisation de Singletons (de préférence) ou de Factories lorsque c'est nécessaire


Moins intuitif pour les nouveaux développeurs et parfois plus compliqué pour l'analyse de code. Nécessité de standardiser l'utilisation à tout le Système pour en retirer le profit
Dissocier davantage le fonctionnel et la technique en déclarant les liens entre les couches en dehors du code
Pattern IOC très pertinent pour les échanges entre les couches de l'application : les DAO sont injectées dans le métier, le métier dans les contrôleurs.
Plateforme de
production
(ou exploitation) :
applications exploitées par les utilisateurs finaux
Site web exposé sur Internet, applications de backoffice dédiés au personnel administratif...
Qualité de service à garantir (temps de réponse, disponibilité...)
Dimensionnement et redondance des plateformes en conséquence
Loadbalancing (répartition de la charge sur plusieurs serveurs),
Failover (reprise du traffic par un serveur de secours lorsque le principal tombe)
CPU, Mémoire, gestion des disques... optimisés
Plateforme de
recette
(ou staging) :
Plateforme présentant des données proches de l'environnement de production
Dimensionnement de la plateforme le plus représentatif de la production.
Validation fonctionnelle par des utilisateurs avant mise en production
Plateforme d'
intégration
continue : architecture présentant tous les composants
Outil d'intégration continue (Jenkins...) :
Récupération des sources sur le référentiel (SVN, GIT...) , analyse, génération de documentation...
Lancement de tests unitaires automatisés
Construction des packages
Déploiement sur des serveurs d'applications
Exploitation des applications par les services informatique
Validation technique par l'informatique avant recette des utilisateurs
Plateforme de
développement
local :
IDE de développement (Eclipse, NetBeans...),
Serveur d'applications (Tomcat, JBoss...) en localhost...
Base de données en local (Mysql, Mongodb...)
Développement et validation des développements par des tests unitaires avant partage sur le référentiel de sources (SVN, GIT...)
1 développeur : 1 environnement de développement
Ingénieur
valide en local
développe
et test
déploie les sources
Référentiel de sources
Intégration continue
JavaDoc
Build de package jar / war...
toutes les nuits ou autre fréquence (nightly build...)
recupère les sources
Serveurs d'applications
d'intégration

webapps
déploie les webapps
(fichiers war)
Poste de développement
Release (version validée)
war
Référentiel
de packages
war
war
Teste les applications
Chef de projet
technique
Serveurs d'applications
de recette

webapps
war
war
war
Serveurs d'applications
de production

webapps
war
war
war
Lance la release
des applications
les packages sont
déployés en recette
effectue les
tests fonctionnels
donne son accord
pour la
mise en production
Responsable
fonctionnel
Utilisateur
final
exploite l'application
war
0.0.1
war
1.0.0
war
0.0.2
les packages sont
déployés en production
Les architectures doivent prendre en considération les divers environnements.
Les adresses (URL, adresses des serveurs...) sont différentes d'un environnement à un autre.
Les adresses sont généralement stockées dans des fichiers de configuration ou de properties JAVA.
Les applications doivent être construites une fois et pouvoir être déployées sur toutes les plateformes
==> éviter de construire autant de versions que d'environnements afin de garantir la cohérence d'une version de l'application.
Les enjeux du multi-environnement

1 : Encapsuler les configurations propres à chaque environnement dans l'application
la liste des environnements est définie et évolue très rarement
les urls et chaînes de connexion (user/mot de passe) ne sont pas sensibles ou ne sont encapsulés que dans des packages à usage interne.
Une variable d'environnement (-DmaVarEnv=production) permet à l'application de connaître l'environnement à utiliser.
Très pratique pour les développeurs et lors des déploiements : pas de besoin de configuration supplémentaire
Les 2 solutions du multi-environnement

2 : Lire les configurations en dehors de la webapp
L'application lit à son initialisation un fichier de configuration situé dans un répertoire local du serveur
La configuration peut parfois être embarquée dans le serveur d'applications lui-même et être accéder par JNDI (Java Naming and Directory Interface)
Les packages n'embarquent pas les mots de passe
Plus contraignant pour les déploiements et pour l'initialisation de projets pour les développeurs : nécessité de configurer et de maintenir le fichier local avec les bonnes urls
Les 2 solutions du multi-environnement
Gestion des exceptions

Nécessité de
superviser les applications en production et détecter les erreurs
mieux maîtriser les points de passage dans le programme et les valeurs lors des phases de développement et d'intégration

Le traçage d'événements dans des fichiers de logs ne doit pas être négligé pour bien mesurer le niveau de service.

Plusieurs frameworks de log en JAVA / J2EE
log4j
: Framework le plus utilisé
java.util.logging
: standard de Java
slf4j
: abstraction des technologies de logs afin de les utiliser indifféremment

INTERDICTION D'UTILISER System.out.println()
Principe des logs et du monitoring
Selon la gravité ou le besoin de la trace, on peut exploiter des niveaux différents.

DEBUG
: message aidant au développement pour :
identifier de façon fine des points de passage dans le programme
écrire les valeurs et mieux comprendre le comportement du programme
Les logs de DEBUG ne doivent pas être utilisées en production par soucis de performance
INFO
: Traces fonctionnelles ou techniques permettant d'identifier des étapes clés du programme :
Initialisation de briques de l'application
Souscription d'un contrat ou autre événement majeur
Les niveaux de trace
WARN
: Point de vigilance pris en compte par le programme n'ayant pas occasionné d'erreur grave pour l'utilisateur

ERROR
: Erreur importante ayant une incidence sur l'expérience utilisateur ou erreur non prévue lors des développements

D'autres niveaux de logs existent mais tendent à disparaître depuis l'apparition du framework SLF4J (qui les a supprimé):
TRACE
: utilisé pour les développements. Est sensé donner plus de détail qu'un debug. A remplacer par DEBUG désormais.
FATAL
: utilisé pour noter que l'erreur a mis fin à l'exécution de l'application ou d'un processus. Supprimé car ce n'est pas aux logs de déterminer si l'application doit être arrêtée. A remplacer par ERROR.
Les niveaux de trace
Chaque entreprise manipule les logs différemment même si des normes générales existent globalement.

Il est important de connaître les normes avant de développer et répondre aux questions :
Quand utiliser log.debug et log.info ?
Quand utiliser log.warn et log.error ?
Quels sont les niveaux de logs écrits dans les fichiers en production ?

Le niveau DEBUG ne doit jamais être tracé en production. Généralement, on trace à partir de :
INFO : Intéressant si on souhaite noter les événements majeurs (signature de contrats) dans les logs.
ERROR : lorsqu'on souhaite focaliser la supervision sur les erreurs plutôt que sur les événements fonctionnels.
Les normes de logs
Utilisation de logs (dans les classes)
Déclaration des
packages slf4j
Utilisation de logs (configuration)
Les logs seront tracés dans la console
en suivant un format (ConversionPattern) prédéfini
Seules Les logs de niveau
ERROR
(ou supérieur) lancées par les classes du package
org.springframework
(ou un de ses subpackages) seront tracées dans la sortie. Aucune sortie (appender) spécifique n'est défini dans la console.

Les logs
DEBUG
,
INFO
,
WARN
et
ERROR
seront tracées pour les classes
fr.mediacourtage.documentation
et ses subpackages....
Les logs
INFO
,
WARN
et
ERROR
seront écrites pour les classes dont les packages ne sont pas gérés par une règle spécifique (<logger>).
Aucune configuration spécifique n'ayant été déclarée dans les <logger>, toutes les sorties iront vers la console.
Utilisation de logs (cas d'utilisation)
Dans la classe
net.sf.itcb.common.ItcbApplication
:
log.info("j'écris en info") -> Pas de trace
log.error("j'écris en error") -> Trace dans la console
Déclaration du logger.
La configuration se fait
en fonction de la classe
Log en DEBUG avec contrôle sur l'activation du niveau de log afin de gagner en performance en production
Log en error avec un message et une exception afin d'écrire la trace dans la sortie
Dans la classe
fr.mediacourtage.documentation.Test
:
log.debug("j'écris en debug") -> Trace dans la console
log.error("j'écris en error") -> Trace dans la console
Dans la classe
com.google.gwt.GwtApplication
:
log.debug("j'écris en debug") -> Pas de trace
log.info("j'écris en info") -> Trace dans la console
Bonnes pratiques des logs
Disposer d'un fichier de configuration de logs spécifique pour chaque environnement : development, integration...
Dans les classes :
Tester le niveau de log via les méthodes isDebugEnabled, isInfoEnabled... afin d'éviter des traitements inutiles sur les environnements n'activant pas certains niveaux.
Éviter les constructions de chaînes et favoriser les templates :
log.debug("Je trace l'id personne : " + idPersonne);
log.debug("Je trace l'id personne : {}" , idPersonne);


Bien configurer les niveaux de logs en allant si besoin dans le détail par package ou par classe. Les logs doivent pouvoir apporter les informations judicieuses pour les administrateurs et développeurs.
Utiliser si besoin un outil d'analyse de logs : Apache Chainsaw...
public int doSomething(int parameter) {
...

b
.delegateDoSomething(parameter, ...);
...
}
public int delegateDoSomething (
int parameter...) {
...

c
.insertSometing(parameter, ...);
...
}
public int doSomething(int parameter) {
...

b
.delegateDoSomething(parameter, ...);
...
}
@Override
public int delegateDoSomething (
int parameter...) {
...

c
.insertSometing(parameter, ...);
...
}
// Appel des méthodes
log.debug(
a
.doSomething(45));
log.debug(
a
.doSomething(46));
log.debug(
a
.doSomething(47));
// Injection des dépendances
b
.setC(
c
);
a
.setB(
b
);

Java gère 2 types d'exceptions :
Les 2 types d ' exceptions possibles
Les
Checked Exceptions
: les exceptions déclarées dans la signature d'une méthode.
public void maMethode
throws UneException
;
==> Obligation pour les méthodes clientes de gérer les exceptions :
en encapsulant l'appel dans un bloc
try / catch
en remontant directement les exceptions en les déclarant dans les
throws
de la méthode

Class B {
}

public int doSomething (
int parameter...) {
try

{
return c.delegate(parameter);
}
catch
(UneException e) {
// TODO gérer l'exception
}

}
Les 2 types d ' exceptions possibles
Les
Runtime

Exceptions
: initialement prévues pour les erreurs techniques de la plateforme : pas d'accès à un serveur, problème d'allocation mémoire, autre problème de la JVM...
Pas de nécessité de gérer l'exception. Les RuntimeException traversent les couches
Class C {
}

public int delegate (int parameter...)
throws UneException {
if(parameter > 45) {
throw new UneException("message"):
}
else {
return parameter + 1;
}
}

public int doSomethingDifferent (
int parameter...)
throws

UneException
{
return c.delegate(parameter);
}
class B {
}

public int doSomething (
int parameter...) {
try

{
return c.delegate(parameter);
}
catch
(UneException e) {
// TODO gérer l'exception
}

}

public int doSomethingDifferent (
int parameter...)
throws

UneException
{
return c.delegate(parameter);
}
Impact sur le cas d'utilisation
class C {






}
}

public int delegate (int parameter...)
throws UneException {
if(parameter > 45) {
throw new UneException("message"):
}
else {
return parameter + 1;
}
}
Cas d ' utilisation

class UneException extends
RuntimeException{
}

...

A l'origine du débat : une problématique à laquelle il faut répondre :
Comment remonter une exception technique d'une couche de bas niveau jusqu'à la couche de plus haut niveau ?
La gestion des exceptions : un vaste débat de salle café
class MaDao {
}
class MonServiceMetier {
}
class monController {
}
SqlException
Faire remonter les exceptions
class MaDao {
}
}
class monController {
}
SqlException
Gérer les exceptions à chaque niveau et les transformer
class MonServiceMetier {
SqlException
SqlException
DataAccessException
BusinessException

public void service (request, response) {
try {
metier.doAction(...);
}
catch(SqlException sqle) {
// TODO gérer l'erreur
}
}

public void doAction (...) throws SqlException {
dao.insert(...);
}

public void insert (...) throws SqlException {
stmt.executeUpdate();
}
Faire remonter les exceptions source en obligeant chaque couche à intégrer la classe d'exception dans la signature de sa méthode
Gérer et transformer les exceptions à chaque couche pour remonter une exception plus cohérente à la couche du dessus

public void service (request, response) {
try {
metier.doAction(...);
}
catch(
BusinessException
be) {
// TODO gérer l'erreur
}
}

public void doAction (request, response)
throws BusinessException
{
try {
dao.insert(...);
}
catch(
DataAccessException
dae) {
throw new
BusinessException
("Message", dae);
}
}

public void insert (request, response)
throws DataAccessException
{
try {
stmt.executeUpdate ();
}
catch(
SqlException sqle
) {
throw new
DataAccessException
(
"message", sqle);
}
}

Aucune des deux n'est idéale.
Laquelle est la bonne ?
Faire remonter les exceptions via les signatures :
Nécessité d'ajouter l'exception dans toutes les couches
Si une nouvelle exception est susceptible d'être remontée par la couche DAO :
il faut modifier toutes les signatures des méthodes des couches amont (métier...)
et gérer la nouvelle exception
sinon cela provoque des erreurs de compilation
Le niveau d'abstraction des couches n'est pas respecté : une couche Controller n'a pas à gérer une exception SQL...

==> Conception douteuse et maintenance trop lourde
Laquelle est la bonne ?
Transformer les exceptions à chaque couche
Le niveau d'abstraction de chaque couche est bien respecté. La couche métier ne saura pas qu'il s'agit d'un problème SQL.
Il apprendra qu'il y a eu un problème d'accès aux données.
==> Mieux !

Si une couche DAO est susceptible de remonter une nouvelle exception, cela obligera la couche amont à gérer le problème.

Dans la pratique, dans quasiment tous les cas, la couche amont ne fait rien de plus que transformer et remonter le problème.
Maintenance trop lourde pour une valeur ajoutée contestable.

==> Peut-on arrêter d'obliger la couche amont à gérer une exception ?
C'est la solution à tous nos problèmes !
Conclusion sur la bonne pratique
Remonter des RuntimeException autant que possible pour garantir la maintenabilité et la facilité d'intégration dans d'autres modules
Rien n'empêchera la couche amont de la gérer spécifiquement avec un try / catch
Continuer à utiliser des Checked Exceptions lorsqu'on souhaite vraiment forcer la méthode appelante à statuer.
Les exceptions remontent toutes les couches de façon transparente dans le code.
Il est bon de disposer d'un gestionnaire d'exceptions au niveau le plus haut pour déterminer le traitement générique des exceptions et les traitements spécifiques à certains types d'exceptions.
Les frameworks des couches Controller mettent à disposition un ExceptionHandler.
Conséquences

Certaines bonnes pratiques sont souvent conseillées :

concernant les transactions :
ouvrir une transaction sur la base de données à chaque appel d'une couche métier
effectuer un commit lorsque le traitement s'est bien déroulé
effectuer un rollback lorsque le traitement a échoué

effectuer une trace dans les logs en début et en fin de méthode (selon l'environnement) pour faciliter la compréhension en développement
AOP... ou la problématique de traitements génériques
class LogDelegateImpl implements AroundActions {










}
Délégation du traitement générique à une classe
class MonServiceMetier {

















}

public void
doAction
(Object...
parameters
) {





dao
.insert(...);
dao
.findAnything(...);
dao
.update(...);
















}
public void
before
(Class
clazz
, String
method
, Object[]
parameters
) {
if(log.isDebugEnabled()) {
log.debug("Entrée dans la classe {}, méthode {} avec les paramètres {}",
clazz
.getClassName(),
method
, arrayToString(
parameters
));
}
}
Coder le mécanisme dans une classe dédiée à cela exploitée par toutes les classes qui en ont besoin
La réponse simpliste à cette problématique
AroundActions
logDelegate
;


public void
setLogDelegate
(AroundActions
logDelegate
) {
this.
logDelegate
=
logDelegate
;
}
public void
afterDone
(Class
clazz
, String
method
, Object[]
parameters
) {
if(log.isDebugEnabled()) {
log.debug("Sortie de la classe {}, méthode {} avec les paramètres {}",
clazz
.getClassName(),
method
, arrayToString(
parameters
));
}
}
public void
afterException
(Class
clazz
, String
method
, Object[]
parameters
, Exception
e
) {
if(log.isDebugEnabled()) {
log.debug("Sortie de la classe {}, méthode {} avec les paramètres {}",
clazz
.getClassName(),
method
, arrayToString(
parameters
), exceptionToString(
e
) );
}
}
try {
logDelegate.before (this.getClass(), "doAction", parameters);

logDelegate.afterDone(this.getClass(), "doAction", parameters);



}
catch (Exception e) {
logDelegate.afterException (this.getClass(), "doAction", parameters, e);



throw e;
}
class TransactionManagerImpl implements AroundActions {






}
public void
before
(Class
clazz
, String
method
, Object[]
parameters
) {
connexion.openTransaction();
}
public void
afterDone
(Class
clazz
, String
method
, Object[]
parameters
) {
connexion.commit();
}
public void
afterException
(Class
clazz
, String
method
, Object[]
parameters
, Exception
e
) {
connexion.rollback();
}
AroundActions
transactionManager
;




public void
setTransactionManager
(AroundActions
tManager
) {
this.
transactionManager
=
tManager
;
}
transactionManager.before (this.getClass(), "doAction", parameters);
transactionManager.afterDone (this.getClass(), "doAction", parameters);
transactionManager.afterException (this.getClass(), "doAction", parameters, e);
Gros problèmes :
Pour 3 lignes fonctionnelles...
environ 20 lignes ajoutées pour les injections, try, catch, appels before...
à multiplier par le nombre de méthodes dans la classe...
Complexité de la maintenance et de la lecture du code
Le code doit se focaliser sur le fonctionnel de la classe !
class LogInterceptor implements MethodInterceptor {












}
Interception AOP du traitement pour déléguer les traitement génériques
class MonServiceMetierImpl {




}

public void
doAction
(Object...
parameters
) {

dao
.insert(...);
dao
.findAnything(...);
dao
.update(...);

}
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
Principe encapsuler les traitements spécifiques au sein de traitements génériques : On parle d' "
interception de traitements
"
L'AOP se fait principalement en utilisant des frameworks qui proposent la fonctionnalité : Spring, Google Guice, AspectJ

On distingue alors :
l'
Advice
: Le code additionnel à ajouter (logguer, gérer les transactions...)
Le
Pointcut
: la (ou les) classe et la (ou les) méthode concernées
L'
Aspect
: la déclaration permettant d'associer l'
Advice
au
Pointcut
La réponse apportée par l ' Aspect Oriented Programming
@Override
public Object
invoke
(MethodInvocation
invocation
) {
try {
if(log.isDebugEnabled()) {
log.debug("Entrée dans la classe {}, méthode {} avec les paramètres {}",
invocation
.getThis().getClass().getName(),
invocation
.getMethod().getName(), arrayToString(
invocation
.getArguments()));
}
return
invocation
.proceed();
}
catch(Throwable t) {
if(log.isErrorEnabled()) {
log.error("Erreur dans la classe {}, méthode {} avec les paramètres {}",
invocation
.getThis().getClass().getName(),
invocation
.getMethod().getName(), arrayToString(
invocation
.getArguments()),
exceptionToString(
e
));
}
}
finally {
if(log.isDebugEnabled()) {
log.debug("Fin de la méthode {}...);
}
}

}
class TransactionInterceptor implements MethodInterceptor {






}
@Override
public Object
invoke
(MethodInvocation
invocation
) {
try {
connexion.openTransaction();
return
invocation
.proceed();
}
catch(Throwable t) {
connexion.rollback();
}
finally {
connexion.commit();
}
}
Exemple de configuration avec Spring
Pointcuts
= Fonctionalité
Advices
= Interceptor
Aspect
= Advisor
class MonController {






}

MonService
monService
;

public void setMonService(MonService monService) { ... }

public void
doAction
(Object...
parameters
) {

monService
.doAction(...);

}
Design pattern utile pour :
factoriser le code générique
concentrer le code de chaque service sur son fonctionnel spécifique
mettre en place des règles génériques et les déployer facilement sur l'ensemble d'une application ou de plusieurs applications
Impératif dans certains cas (et toujours bonne pratique) : utiliser l'interface dans la classe appelante et faire l'interception sur la classe d'implémentation
Possibilité d'utiliser l'AOP aussi pour d'autres besoins :
appeler un webservice : intercepter l'appel à une interface (sans implémentation), transformer l'appel en requête SOAP, transformer la réponse en objet et retourner en objet.
ajouter une couche de sécurité : Effectuer le traitement uniquement si certains conditions sont réunies...
AOP : Synthèse
Aspects positifs :
Généralisation apportée dans une classe séparée
Possibilité de déterminer précisément les
traitements qui font l'objet du traitement "Around"
Conteneur léger
But : faciliter les échanges entre les couches par des déclarations

Basé sur le Pattern IOC, Spring gère les injections de dépendances

Spring fournit des modules pour gérer plusieurs besoins techniques :
Accès aux données et transactions Bdd : Spring Data
Consommer ou exposer un webservice : Spring WS
Gérer la sécurité des accès : Spring Security
Gérer les appels web : Spring MVC
...
Avec injection de dépendances
class AImpl implements A {
}
protected
B

b
;

public
AImpl
() {
}
public
setB
( B
b
) {
this.
b
=
b
;
}
interface B {
}

public int delegateDoSomething (
int parameter...);
interface BImpl implements B {
}
protected
C c
;

public
BImpl
() {}

public
setC
(
C c
) {
this.
c
=
c
;
}
Configuration Spring : monapplication_context.xml
<beans>
<bean
id=
"cSingleton"
class=
"
com.mycompany.myproject.CImpl
">
<property
name=
"
uneConstante
"
value=
"
Une valeur par défaut
">
...
</bean>
<bean
id=
"bSingleton"
class=
"
com.mycompany.myproject.BImpl
">
<property
name=
"
c
"
ref=
"cSingleton">
</bean>
<bean i
d=
"aSingleton"
class=
"
com.mycompany.myproject.AImpl
">
<property
name=
"
b
"
ref=
"
bSingleton
">
</bean>
</beans>
public int doSomething(int parameter) {
...

b
.delegateDoSomething(parameter, ...);
...
}



@Override
public int delegateDoSomething (
int parameter...) {

c
.insertSometing(parameter, ...);
}
class MainProgram {
}
public void main (...) {
...
ClassPathXmlApplicationContext
appContext
=
new ClassPathXmlApplicationContext(
new String[] {"classpath:monapplication_context.xml"} );




...
}
A
a
=
appContext
.getBean("aSingleton", A.class);
a
.doSomething(1);
a
.doSomething(2);
a
.doSomething(3);
Mémoire vive
1 instance de Cimpl
1 instance de Bimpl
l'instance de Cimpl injectée dans l'instance BImpl
1 instance de Aimpl
l'instance de BImpl est injectée
dans l'instance AImpl
Utilisation de MVC et de Spring MVC
Principe du MVC appliqué au WEB en architectures N-TIERS
View
Model
Controller
Utilisateur
Soumet un formulaire
La JSP fournit un flux de sortie
la vue est transmise au controller
Le controller communique avec le model
Le Controller construit un bean et le transmet à la couche service
La couche Service retourne une réponse
Le controller prépare un bean et le transfère à une JSP
La requête est transformée en objet et est transmise au controller
Application du modèle MVC dans notre architecture
View
Jsp
Request
Response

Controller
La servlet appelé aussi le Front Controller ou Main Controller
Les Controllers spécifiques à chaque URL

Model
Le Bean à envoyer à la couche Service
La couche Service et les couches avales
Le Bean retourné par la couche Service
Bonnes pratiques et rôles
View
La JSP ne fait pas de traitement de données et ne fait pas d'appel direct à la base de données
Uniquement de l'affichage des données stockées en attribut de request par le controller
Envoi des données via un POST ou un GET en appel HTTP standard ou en HTTP Ajax

Controller
Récupération des objets envoyés par la VIEW
Transformation éventuelle en objets MODEL pour communiquer avec les couches Services
Pas de traitement métier. Délégation au Model (couche Service)

Model
Traitement métier sur la base d'un bean en entrée et en sortie
Frameworks MVC standards
Spring MVC
Struts
JSF
Tapestry
Mais il y a aussi d'autres solutions...
Les architectures RIA (Rich Interface Application)
Google Web Toolkit
Vaadin (basé sur GWT)
Angular JS (codé en Javascript)
Les architectures Client Server AJAX / REST
un client web utilisant Javascript et AJAX pour communiquer avec un serveur
un serveur REST
Spring MVC
Module de la suite Spring pour :
proposer une implémentation du pattern MVC pour distribuer des pages WEB
mais aussi tout autre flux : JSON, PDF...

Module servant de base à plusieurs autres projets Spring
Spring WS
...

2 modes de créations de Controllers
par déclarations XML
par annotations

Mise à disposition de taglibs pour faciliter l'exploitation des beans dans les JSP
Principe de gestion d'une requête avec Spring MVC
Configuration de la DispatcherServlet dans le web.xml
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>redirect.jsp</welcome-file>
</welcome-file-list>

Exemple de Controller
Exemple de configuration Spring
Exemple de code JSP
MVC
Spring MVC
et dans le code
<html>
<head>
<title>Welcome Page</title>
</head>
<body>
${welcomeMessage}
</body>
</html>
et la possibilité d'utiliser des taglibs
http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/view.html
Travail de construction
de JSP facilité
Forte dépendance de la vue au framework
Conseil : utiliser les JSTL standard JAVA
http://docs.oracle.com/javaee/5/tutorial/doc/bnake.html
Full transcript