Développement d'applications Web & Distribuées avec JavaEE

Introduction

Installation de Jetbrains IntelliJ IDEA et Jetbrains Datagrip

Licence

IntelliJ et Datagrip sont des outils professionnels sous licence. Heureusement, Jetbrains permet aux étudiants de bénéficier de licences gratuites pour tous ses outils. Elles sont accessible via le Github Student Pack. Une fois le student pack en main, rdv ici pour créer un compte Jetbrains en utilisant son compte Github.

Avec JetBrain Toolbox

Jetbrains Toolbox est un petit utilitaire très pratique qui permet d'installer et mettre à jours les logiciels JetBrains en un clic.

Installer Jetbrains Toolbox

Téléchargez et installez Jetbrains Toolbox

Installer IntelliJ

Exécutez JetBrains Toolbox puis faites un clic droit sur son icône dans la barre d'état système de Windows. Trouvez ensuite IntelliJ et Datagrip dans la liste des applications proposée. Cliquez sur "Install" et attendez la fin du téléchargement.

Sans JetBrains ToolBox

Téléchargez IntelliJ via ce lien et datagrip via ce lien, puis exécutez l'installateur. Suivez ensuite les instructions d'installation.

Installer une base de donnée MySQL

Laragon est un outil qui package plusieurs outils pour le développement web. Nous allons l'utiliser la base de donnée MySQL. Téléchargez Laragon via ce lien,puis exécutez l'installateur. Suivez ensuite les instructions d'installation.

Configurer Laragon

Lancez Laragon puis faites clic droit sur le fond > MySQL > Change root password. Mettez un mot de passe et conservez le bien il servira à se connecter à la base de donnée. Lancez ensuite le serveur en faisant clic droit sur le fond > MySQL > Start MySQL

Créer une base de donnée

Vous pouvez créer une base de donnée pour chacun de vos projet en faisant : clic droit sur le fond > MySQL > Create Database, puis donnez lui un nom.

Installer une base de donnée Oracle

  • Télécharger Oracle
  • Extraire
  • Exécuter et suivre le programme d'installation -> Attention bien se rappeller le mot de passe admin qu'on a miss
  • Ouvrir SQL plus
  • Se connecter le compte system et le mot de passe que vous avez setup dans l'installation
  • Exécuter la commande suivante :
Exec DBMS_XDB.SETHTTPPORT(3010);
  • Créer votre compte utilisateur :
CREATE USER nom_utilisateur IDENTIFIED BY mot_de_passe;
GRANT DBA TO nom_utilisteur;

Se connecter à une base de donnée avec Datagrip

Lancer Datagrip puis cliquer sur le bouton "+" :

2020-09-12-17_17_24-.png

Et choisissez "Datasource > MariaDB" :

2020-09-12-17_18_48-.png

Au bas du formulaire cliquez sur "Download missing driver file". Remplissez ensuite le formulaire comme suit :

  • Name : nom de votre projet
  • Host : laissez localhost
  • port : laissez 3306
  • User : root
  • Password : Le mot de passe que vous avez configuré sur la base de donnée MySQL de Laragon
  • Database : Le nom de la base de donnée que vous avez créé sur Laragon

Faites ensuite "Test Connection" afin de voir si tout est bien configuré, puis fait "Apply". Votre base de donnée est apparue dans la liste des bases de donnée à gauche de l'interface. Pour ouvrir une session SQL faites Clic droit sur le nom de votre base > New > Query Console :

2020-09-12-17_25_20-.png

Vous avez donc une console dans laquelle vous pouvez écrire des scripts SQL. CTRL + ENTER pour exécuter votre script.

Conteneur de Servlets Apache Tomcat

Apache Tomcat est un conteneur de servlets. Il sert à exécuter des composants de serveur HTTP écrits en Java, les servlet, sur lesquels nous reviendront plus tard.

  • Télécharger Apache Tomcat 10 via ce lien (64-bits windows ZIP)
  • Extraire l'archive
  • Stocker dans son dossier d'outils (ex : "C://tools/)

Créer un projet JakartaEE avec IntelliJ

Créer le projet

Créez un nouveau projet en utilisant le template "Java Entreprise" :

screenshot-2022-02-21-094421.png

Explications :

  • Project Template : type de projet, ici une application web "à pages" basée sur Servlets et JSP
  • Build System : outils de build utilisé, nous allons utiliser Maven
  • Test Framework : bibliothèque de tests
  • Group : GroupId de votre artéfact, par convention, votre domaine à l'envers
  • Application server : serveur d'application sur lequel votre application sera déployée

Ajouter un serveur d'application

Pour ajouter un nouveau serveur d'application sur votre IntelliJ, cliquez sur "New" et séléctionner le type de server d'application que vous voulez utiliser. Dans ce tutoriel, nous allons utiliser Tomcat dans un premier temps, puis Wildfly dans la partie avancée.

capture-decran-2021-05-23-102509.png

Ensuite renseignez simplement le répertoire de votre serveur d'application :

capture-decran-2021-05-23-102536.png

Faites "suivant".

Dépendances JakartaEE

Sur l'écran suivante, dans les dépendances, nous devons choisir quelles spécifications de la plateforme JakartaEE notre application va utiliser. Il faut aussi se référer pour la version à prendre, à la version de notre serveur d'application :

screenshot-2022-02-21-094712.png

Par exemple, pour les versions 10.0.x (ce qui est mon cas à l'heure où j'écris ces lignes), il faut prendre la spécification de servlet en version 5.0.

Pour accèder à la dépendance de la Servlet API 5.0, vous devez positionner la version de JakartaEE à 9 :

capture-decran-2021-05-23-102321.png

Ces dépendances ne vont télécharger ques les interfaces de la spécification dans votre projet (pour pouvoir les utiliser dans le code), les implémentations seront fournies par votre serveur d'application.

Tester le projet

IntelliJ vous a généré un projet Maven avec les dépendances JakartaEE demandées ainsi, qu'une configuration de déploiement pour votre serveur d'application. Vous pouvez simplement lancer votre projet en cliquant sur le boutun "Run" et la page racine de votre application devrait s'ouvrir :

capture-decran-2021-05-23-102719.png

Base des applications JavaEE

Base des applications JavaEE

Java EE

Qu'est ce que JavaEE ?

Java EE ou J2EE ou Jakarta EE est un ensemble de spécifications de différentes API orientée pour le développement d'applications professionnelles et d'entreprise. Chacune de ces spécifications d'API répond à un besoin courant de ce type d'application, comme par exemple, accèder à une base de donnée, répondre à des requêtes HTTP, etc ...

Les Serveurs d'applications

Les serveurs d'application sont les environnement d'exécutions pour les applications JavaEE. Ils implémentes certaines ou toutes les spécifications d'API. Quelques exemples :

  • Référence : Oracle Glassfish
  • Commerciaux :
    • WebSphere (IBM)
    • Weblogic (Oracle)
  • Open source :
    • Tomcat (TomEE)
    • JBoss/Wildfly

Ainsi, les applications JavaEE ne sont pas exécutées comme les applications JavaSE via une méthode main. Elles doivent être packagées puis déployées sur un serveur d'application. Heureusement, notre IDE rend cette tâche triviale.

Base des applications JavaEE

Servlet API

Les servlets

La Servlet API est une brique fondamentale de la spécification JavaEE. Elle permet de développer des composants gérant des requêtes HTTP. L'instanciation des servlets n'est pas gérées par le programmeur, mais par ce que l'ont appelle le conteneur de servlets. Il s'agit d'une partie du serveur d'application qui orchestre le cycle de vie des servlets ainsi que les threads nécessaires à la gestion des requêtes.

Pour créer une servlet, il suffit de créer une classe qui étend la classe HttpServlet et de lui affecter l'annotation @WebServlet avec en paramètre la route qui est être gérée par la servlet.

@WebServlet("/helloworld")
public class HelloWorldServlet extends HttpServlet {
    
}

Dans une servlet vous pouvez redéfinir certaines méthodes, notamment doGet et doPost. Ces méthodes sont appelées par le conteneur lorsqu'une requête HTTP est faite sur la route de la servlet. doGet gère les requête avec la méthode HTTP GET et doPost gère les requêtes avec la méthode HTTP POST.

@WebServlet("/helloworld")
public class HelloWorldServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

Les paramètres de cetes méthodes, représentent respectivement la requête HTTP et la réponse HTTP.

Objet requête

Paramètres

L'objet requête permet de récupérer des paramètres soit passés dans l'URL de la requête dans le cas d'une requête HTTP GET, ou paramètres d'un formulaire (passés dans le Body de la requête au format FormData) :

String monParam = request.getParameter("nomDuParam");

Session

L'objet requête permet également de récupérer la session du client. Dans la session, il est possible de stocker des variables et de les récupérer. La session est propre à un client.

// Récupérer la session
HttpSession session = request.getSession();	

// Ecrire une variable dans la session
session.setAttribute("profil", profil);

// Récupérer une variable de session
ProfilUtilisateur profil = (ProfilUtilisateur) session.getAttribute("profil");

Objet réponse

L'objet réponse permet d'écrire du texte dans le Body de la réponse HTTP :

response.getOutputStream().println("Hello world");

On peut également faire une redirection vers une autre route de l'application :

response.sendRedirect("/helloworld");

Première Servlet

Vous pouvez désormais créer une servlet "Hello world" :

@WebServlet("/helloworld")
public class HelloWorldServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getOutputStream().println("Hello world");
    }
}

Ensuite lancez le projet, une page web dans votre navigateur devrait apparaître. Ajoutez /helloworld à la fin de l'URL et vous devriez voir le message hello world :

capture-decran-2020-11-25-134614.png

Récupération d'un paramètre GET

Pour récupérer un paramètre GET, on utilise la méthode getParameter de la requête :

@WebServlet("/helloworld")
public class HelloWorldServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("name"); 
        response.getOutputStream().println("Hello " + name + "!");
    }
}

Relancez votre projet et ajoutez un paramètre GET dans l'URL !

Soumission d'un formulaire

Nous allons maintenant voir comment gérer une soumission de formulaire avec une servlet. Dans le dossier webapp, vous devriez trouver une page index.jsp. Considérer là comme une simple page HTML, nous verrons les JSP dans la partie d'après. Si elle n'est pas présente créez simplement une page index.html. Ecrivez maintenant un formulaire POST simple :

<form method="post" action="/helloworld">
  
  <input type="text" name="monParam"/>
  <button type="submit">Envoyer</button>
  
</form>

Dans ce formulaire, l'action est mappée sur l'URL de notre servlet. Il faut donc que notre servlet implémente la méthode doPost pour gérer les requêtes POST. On peut ensuite récupérer le paramètre POST de la même façon que les paramètres GET :

@WebServlet("/helloworld")
public class HelloWorldServlet extends HttpServlet {

  	...
  
     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("monParam"); 
        response.getOutputStream().println("Hello " + name + "!");
    }
}


Base des applications JavaEE

Filtres

Les filtres permettent d'intercepter des requêtes HTTP avant qu'elles arrives à leur Servlet afin d'exécuter de la logique. Cela est notamment utile pour restreindre l'accès à certaines routes à des utilisateurs non authentifiés ou ne possèdant pas le bon rôle.

Créer un filtre

Pour créer un filter, il faut créer une classe qui implémente l'interface Filter et annotées avec @WebFilter. En paramètre de l'annotation, il faut passer le paramètre urlPatterns, pour préciser les routes qui seront filtrée par ce filtre.

@WebFilter(urlPatterns = "/admin/*")
public class AdminFilter implements Filter {


    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
      
    }

    public void init(FilterConfig config) throws ServletException {

    }
  
      public void destroy() {
    }

}

Par exemple ce filtre va intercepter les requêtes sur toutes les routes qui commencent par "/admin/".

Actions dans un filtre

Dans un filtre on peu récupérer la requête et la réponse HTTP par un simple cast :

var request = (HttpServletRequest) req;
var response = (HttpServletResponse) resp;

On peut ensuite accèder à la session pour vérfier des informations ou faire une redirection. Pour autoriser la poursuite de la requête normalement utilisez :

chain.doFilter();

Et pour retourner un code HTTP particulier (401 Unauthorized ou 403 Forbidden) et arrêter là la requête :

response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
Base des applications JavaEE

JDBC

JDBC pour Java DataBase Connectivity est une spécification d'API pour accèder à une base de donnée relationnelle depuis une application Java. Pour se connecter à une base de donnée, il faut un Driver, qui implémente JDBC pour un moteur de base de donnée relationnelle donné. Par exemple il en existe un pour Oracle, un pour MySQL, pour PostgreSQL, etc ...

Installation du Driver

Pour installer le Driver JDBC pour MySQL, ajoutez la dépendance suivante dans votre pom.xml :

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.22</version>
</dependency>

Pour Oracle utilisez cette dépendance :

<dependency>
  <groupId>com.oracle.database.jdbc</groupId>
  <artifactId>ojdbc8</artifactId>
  <version>21.1.0.0</version>
</dependency>

N'oubliez pas refresh vos dépendances Maven après avoir modifié votre pom.xml !

Connexion à la base de donnée

Pour se connecter à une base de donnée utilisez la méthode static DriverManager.getConnexion() en passant en paramètre la chaine de caractère de connexion ainsi que les identifiants.

Pour MySQL :

Class.forName("com.mysql.cj.jdbc.Driver")
Connection connexion = DriverManager.getConnection("jdbc:mysql://localhost:3306/nomDeLaBase","root", "mot de passe");

Si MySQL vous lance une erreur de TimeZone, ajoutez ceci à la fin de l'URL JDBC : ?useLegacyDatetimeCode=false?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC.

Pour Oracle :

Class.forName("oracle.jdbc.driver.OracleDriver");
Connection connexion = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe","root", "mot de passe");

Il est recommandé de faire un singleton de l'instance de la connexion à la base de donnée.

Interactions avec la base de donnée

Requêtes

L'objet Statement permet d'exécuter des requêtes SQL :

Statement statement = connexion.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM Utilisateurs");

Le ResultSet est un itérateur sur les enregistrements contenues dans la réponse à la requête. On peut l'exploiter en itérant dessus grâce à la méthode next() :

while (rs.next()) {
  Utilisateur utilisateur = new Utilisateur();
  utilisateur.setPrenom(rs.getString("PRENOM"));
  utilisateur.setNom(rs.getString("NOM"));
  listeUtilisateur.add(utilisateur);
}

On peut en ensuite récupérer le contenu de l'enregistrement courant avec des méthodes comme getString(), getInteger(), etc ... en leur le passant le nom de la colonne dans la base.

Les classes Statement et ResultSet doivent toutes les deux êtres fermées après leur utilisation car il s'agit de ressources non managées. Pour une syntaxe concise on peut utiliser le "Try With Resource" :

try (Statement statement = this.connection.createStatement();
     ResultSet result = statement.executeQuery("SELECT * FROM Utilisateurs");) {
      while (result.next()) {
          Utilisateur utilisateur = new Utilisateur();
          utilisateur.setPrenom(rs.getString("PRENOM"));
          utilisateur.setNom(rs.getString("NOM"));
          listeUtilisateur.add(utilisateur);
      }
} catch (SQLException e) {
     System.err.println("Erreur à l'exécution de la requête SQL");
}

Requêtes DML

Pour modifier des données dans la base avec des commandes INSERT,UPDATE ou DELETE on utilise la méthode executeUpdate() de la classe Statement :

int modifiedRows = statement.executeUpdate("DELETE FROM Utilisateurs WHERE NOM = 'Shepard'");

executeUpdate() retourne le nombre de lignes modifiées par la requête.

SQL Dynamique

Il est possible de faire des requêtes préparées en utilisant la classe PreparedStatement :

PreparedStatement preparedStatement = connexion.prepareStatement("DELETE FROM Utilisateurs WHERE NOM = ?");

On peut ensuite affecter des paramètres :

preparedStatement.setString(1,"Shepard");

Enfin, on exécute le PreparedStatement en utilise soit executeUpdate() soit executeQuery() selon qu'il s'agisse d'une commande DML ou d'une requête.

Base des applications JavaEE

JSP/JSTL

Les Java Server Pages

Ecrire une interface HTML dans une string Java n'est pas très pratique. C'est pourquoi il est possible d'utiliser pour ce faire les JSP. Les Java Server Pages sont des templates de vues qui sont rendus coté serveur. Lorsque vous renvoyez une JSP depuis une Servlet, elle va être interprétée et le code HTML résultant sera placé dans le Body de la réponse HTTP. Ces vues sont templatables par du code Java mais c'est une mauvaise pratique car du code Java exprimant de la logique ne devrait pas résider dans une vue. C'est pourquoi il est recommandé d'utilise JSTL, la JSP Standard Tag Library.

Pour créer une JSP qui sera renvoyées par une servlet, créer un fichier .jsp à dans le dossier src/main/webapp/WEB-INF. Les fichiers de styles et de scripts sont à mettre directement dans le dossier src/main/webapp et sont accessibles à partir de l'URL racine du projet.

Il est possible de passer des objets Java à une JSP depuis une servlet :

request.setAttribute("nom", monObjet);

Pour renvoyer une JSP depuis une servlet, utilisez le code suivant :

this.getServletContext().getRequestDispatcher("/WEB-INF/maJsp.jsp").forward(request, response);

JSP Standard Tag Library

Installation

Pour installer JSTL, ajouter la dépendance au pom.xml de votre projet :

<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>jakarta.servlet.jsp.jstl</artifactId>
    <version>2.0.0</version>
</dependency>

Pour importer la JSTL dans une JSP utilisez les balises suivantes :

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

Balise de la JSTL

Afficher une variable

La balise <c:out> permet d'afficher une variable passée par la Servlet :

<c:out value="${ variable }">Valeur par défaut si la variable est null</c:out>

Cette balise évalue en fait une expression écrite dans un langage appelé EL pour Expression Language. C'est un langage simple qui permet de faire des petites opérations sur les variables. Il permet d'accèder par leur nom aux variables passés à la JSP par la servlet.

Il permet également d'accèder à un champs d'un objet Java si celui ci possède un Getter correctement nommé en utilisant l'opérateur .. EL supporte également les opérateurs arithmétiques et logique ainsi que les expressions ternaires. Le mot clé empty permet de vérifier si une valeur est nulle.

Itération sur une liste

La balise <c:forEach> permet d'itérer sur les éléments d'une liste :

<c:forEach items="${ maListe }" var="monElement" varStatus="status">
    <p>N°<c:out value="${ status.count }" /> : <c:out value="${ titre }" /> !</p>
</c:forEach>

Ici la variable monElement dansl'attribut var de la boucle correspond à l'élément courant de la liste. Quant à la variable status (optionnelle) définie dans l'attribut varStatus elle permet d'obtenir des informations sur l'élément courant de la boucle et possède des propriétés comme :

  • index : l'indice de l'élément
  • first : vrai si l'élément est le premier
  • last : vrai si l'élément est le dernier

Rendu conditionnel

La balise <c:if> permet de faire du rendu conditionnel :

<c:if test="${ variable == '1' }">
    C'est vrai !
</c:if>

La contenu de la balise n'est rendu que si l'expression EL dans l'attribut test est évaluée à true.

Rendu conditionnel à choix multiple

La balise <c:choose> permet de faire du rendu conditionnel à choix multiples :

<c:choose>
    <c:when test="${ variable = '1' }">C'est égal à 1</c:when>
    <c:when test="${ variable = '2' }">C'est égal à 2</c:when>
    <c:when test="${ variable = '3' }">C'est égal à 3</c:when>
    <c:otherwise></c:otherwise>
</c:choose>