Tp2 IMT Mines Alès Développement Web

1/ Ajout d’une feuille de style

Dans Eclipse, pour ajouter un fichier CSS (ou autre), allez sur le dossier WebContent et le sous dossier css (créé le dossier s’il est manquant clic droit sur WebContent > New > Folder) puis faire clic droit pour ouvrir le Menu:

Dans le Menu on peut choisir New > Other puis dans la longue liste soit choisir soit chercher ici un fichier CSS. Cela devrait donc créer dans le bon dossier WebContent\Css\ le fichier style.css.

L’ajout d’une feuille de style permet de proposer une nouvelle mise en forme. Ajouter le fichier style.css contenant le code suivant dans le dossier css du dossier WebContent:

div#central{
  position:absolute;
  left: 50%;
  top: 10%;
  width: 800px;
  height: 800px;
  margin-top: -50px;
  margin-left: -400px;
  color: black;
  background-color:rgb(200,220,200);
  padding: 20px;
}
ul {
  list-style: none;
  margin: 0;
  padding: 4px 4px 4px 4px;
}
ul > li{
   display: inline;
}
div#menu{
  position:relative;
  left:10%;
  width:80%;
  background-color:rgb(200,240,220);
}

Il faut ajouter deux div dans la JSP avec comme « id » central et menu, le div « menu » doit être inclus dans central et appeler le fichier style.css dans le tag <head> :

<link rel="stylesheet" type="text/css" href="css/style.css">
<div id="central">
 <div id="menu">
      <!-- Zone pour le menu -->
 </div>

    <!-- Zone pour le contenu -->
</div>

2/ Zone de menu

La zone menu va utiliser une liste d’hyperlien pour présenter un menu (voir le site alsacreation pour les étapes de construction en CSS mais arrêté vous avant l’étape « Un menu graphique, avec rollover mais sans javascript »).

Notre objectif ici est de vous montrer quelques possibilités du CSS pour la modification de tag d’une liste.

<ul><li><a href="#">Menu 1</a></li><li><a href="#">Menu 2</a></li></ul>

Après la création du menu avec la feuille de style placer ce code dans la zone div menu pour que sa forme soit similaire à l’image ci-dessous.

Je vous invite à modifier les couleurs en CSS et la mise en forme à votre gout, elle est stratégiquement moche pour vous motiver en ce sens 😉

Pour l’affichage des différentes zones, je vous recommande d’utiliser les outils intégrés avec votre navigateur pour sélectionner un élément et voir son code correspondant:

Sur cette image, nous voyons qu’en sélectionnant en bas les tags, leur équivalent sera surligné dans la partie du haut.

Autre exemple avec le site www.mines-ales.fr (oui on utiliser l’inspecteur de code Html sur tout site web), on peut sélectionner l’outil qui est représenté par une flèche puis sélectionner un élément HTMl ce qui nous indique en correspondance le tag html correspondant à cet élément et on voit aussi en partie droite de l’interface la liste des réglages CSS qui s’y applique:

Modifier les marges et les couleurs à votre convenance pour utiliser les différentes règles du CSS.

Notre menu au final devrait être plutôt de cette forme pour permettre des appels à notre contrôleur:

<ul>
  <li>
   <a href="./Controleur?action=pageAddTodo">Add Todo</a>
  </li>
  <li>
      <a href="./Controleur?action=xml">Liste xml</a>
  </li>
  <li>
    <a href="./Controleur?action=json">Liste Json</a>
  </li>
</ul>

Mais on va conserver la forme initiale pour ajouter à terme par exemple des évènements en javascript dans le TP dédié à javascript.

3/ Découverte du framework bookstrap avec une mise en forme à récupéer :

Je vous invite à comprendre comment fonctionne la mise en forme du framework Bootstrap Css en utilisant par exemple le jumbotron comme exemple: Jumbotron . 

Si vous ne vous sentez pas à l’aise en Css, vous pouvez ne pas le réaliser mais au moins regarder l’enchainement des actions ci-dessous notamment l’affichage de l’onglet Network/Réseau dans les outils de développement.

L’idée serait de placer votre liste de Todo dans la colonne de gauche, le formulaire pour ajouter un nouveau todo au milieu (utiliser l’inspecteur Html pour trouver le tag gérant cette zone peut être un tag <div class= »col-md-4″> qui mériterai un attribut id) :

Pour récupérer le code HTML, faire clic droit sur la page pour afficher la page HTML.

Sinon ouvrer encore une fois les Web Dev Tool du navigateur et dans l’onglet Network/Réseau vous avez la liste des fichiers téléchargés par cette page HTML.

Sélectionner les éléments Css et JS pour lister tous les fichiers et les ouvrir individuellement dans une nouvelle page pour les sauvegarder ensuite:

Il ne vous reste plus qu’à reconstruire la liste des fichiers dans votre dossier WebContent et à corriger les URL d’appel de ces fichiers dans le tag <header> de votre page JSP.

4/ Utilisation des logs

Les logs sont une solution élégante pour gérer 2 problèmes classiques, plusieurs niveaux d’informations techniques et plusieurs sources d’informations techniques.

Quand vous développez, vous avez tendance à faire une écriture dans la console via des classiques:

System.out.println("Erreur " + e);

En développement Web avec Tomcat, si vous faites des System.out.println, toutes les sorties console vont au niveau de Tomcat dans un fichier appelé catalina.out.

Si vous avez plusieurs applications sur votre serveur tomcat, toutes les sorties vers la console seront mélangées.

On peut aussi utiliser la commande vers les sorties erreur mais cela ne fait que 2 niveau System.out ou System.err.

System.err.println("Erreur " + e);

Mais encore une fois, nous n’avons que 2 niveaux séparrant les informations et tout est mélangé.

En production, les logs permettent de gérer les sorties d’informations de l’application en se limitant au maximum, et au contraire en développement, on peut avoir besoin d’un niveau très important de détails des logs.

Log4j est un framework de Log qui permet de gérer plusieurs destination aux commande de log vers la console et/ou des fichiers et/ou une base de données et/ou un serveur de log.

Nous pouvons aussi démarrer un projet avec le niveau de log que nous voulons.

Pour installer log4j, télécharger ma version disponible en version 1.2.17 sur le site habituel : http://www.master-ctn.mines-ales.fr/install/M1-Web/commun/. Vous placez ce jar dans un dossier lib (à créer) dans le dossier WEB-INF du dossier WebContent.

Nota: vous pourrez mettre d’autres librairies .jar à déployer dans votre projet web dans ce dossier pour que chaque Application Web disposent de ses propres jars. Ci-dessous un exemple d’un projet Web où j’ai eu besoin d’une petite liste de jar:

Nota 2:  Une autre option est d’utiliser le dossier lib dans le dossier tomcat pour indiquer les jars disponible pour tous les projets déployés mais dans ce cas, les jars seront identiques pour toutes vos applications Web développés. Le choix est toujours soit d’un côté de normaliser pour que toutes les applications utilisent la même bibliothèque soit au contraire d’individualiser pour que chaque application disposent de ses propres bibliothèques. C’est une décision de mise en production.

NOTA : Eclipse ne scanne pas les nouveaux fichiers comme votre fichier de log, il faut faire un le refresh du projet, sur le projet, clic droit Menu > Refresh    ou la touche F5 comme votre navigateur. Bien sur il faut généré un message pour le logger sinon le fichier ne sera pas créé

Pour configurer le logger, on va créer un fichier log4j.properties dans le dossier Java Resources > src (il n’a pas à être dans un package ce n’est pas un fichier java juste un fichier de configuration). Pour créer un fichier texte, faire clic droit sur le dossier > New > Other …> General > Untitled Text File.

Entrez les informations suivantes dans le fichier et sauver le dans votre projet, dans le dossier src avec comme nom log4j.properties.

Le fichier log4j.properties doit contenir ces informations:

log4j.rootLogger=DEBUG,stdout,file

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n


log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:/Developpement/workspace_jee/BoostrapSample/out.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%-7p %d [%t] %c %x - %m%n

NOTA: BoostrapSample est le nom de mon projet, à modifier selon votre nom de projet bien sûr.

La première ligne indique que le niveau de log est DEBUG et qu’il y a 2 loggers: stdout et file.

Ensuite nous avons 2 sorties de notre logger, log4j.appender.stdout et log4j.appender.file. Nous pourrions envoyer des log vers d’autres sorties comme une base de données, un serveur de log, des emails, etc.

Les lignes commençant par log4j.appender.stdout indique les réglages pour le logger stdout soit une sortie d’information sur la console de l’ordinateur (là où va habituellement le System.out.println).

Les lignes commençant par log4j.appender.file indique les réglages pour le logger file soit une sortie dans un fichier. Il vous faut corriger mon exemple pour que le fichier puisse être créer dans le dossier qui correspond à votre workspace ( vous n’avez peut être pas de dossier: c:/Developpement/workspace_jee/BoostrapSample/ ).

L’instruction ConversionPattern indique la forme de la sortie:

La date: %d{ABSOLUTE}
Le niveau de log: %5p
Le nom du logger: %c{1}
La ligne du log: %L
Le message: %m

Vous pouvez trouver des exemples de pattern avec log4j

Pour utiliser le logger dans une classe, on va simplement créer une instance log de type org.apache.log4j.Logger:

private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger("BoostrapSample");

Nota: j’utilise le chemin absolut de la classe en spécifiant sont package devant (org.apache.log4j) car il existe d’autres solutions de log et Tomcat utilise d’ailleurs pour son utilisation interne org.apache.commons.logging. Il peut donc y avoir conflit entre les 2 systèmes de Log. Nous avons ici une démonstration de la force/faiblesse de Java qui va foisonner de solutions pour proposer une variété de réponses mais qui peut gêner par leur multiplicité, nous obligeant à gérer la situation.

Au moment où on veut logger une information, il suffit d’appeler l’instance avec le niveau de sévérité du message  et de passer en paramètre le message.

Le niveau de sévérité permet de différencier l’importance d’un message de log.

Il y a habituellement 6 niveaux du plus critique au moins critique:

  1. fatal
  2. error
  3. warm
  4. info
  5. debug
  6. trace

Si fichier de configuration de log4j indique que je dois logger du niveau info, alors les messages de sévérité fatal, error, warm et info seront envoyés.

Je vous renvoie sur cet excellent article qui indique quelques bonnes pratiques.

Par exemple, si je veux faire un log après l’ajout d’un nouveau Todo, le niveau que je vais choisir est debug, un niveau pour le débugage qui ne sera pas envoyé par la configuration de log4j en production.

// Action1 addTodo 
if ("addTodo".equals(action)) { 
  logger.debug( "addTodo " + texte );
}

ou en suivant les bonnes pratiques conseillé par l’article de Setu Patani en utilisant le remplacement de symbole avec la méthode String.format:

// Action1 addTodo 
if ("addTodo".equals(action)) { 
  logger.debug( String.format( "addTodo %s", texte ) );
}

La version 2 de log4j  permet directement de faire l’opération avec le symbole de remplacement mais nous nou contenterons de la version 1 de log4j pour plus de simplicaité

// Action1 addTodo 
if ("addTodo".equals(action)) { 
  logger.debug( "addTodo {}", texte );
}

On peut donc faire des messages de différent niveau de criticité:

logger.trace("Niveau le moins important");
logger.debug("Niveau au dessus du moins important");
logger.info("Niveau habituel de fonctionnement");
logger.warm("Erreur non bloquante");
logger.error("Niveau Erreur classique");
logger.fatal("Niveau Erreur maximum");

Au final on peut utiliser qu’un nombre limité de niveaux d’erreurs ou au contraire ajouter ses propres niveaux. L’important est d’éviter de saturer d’erreurs le fichier de tomcat avec tout type de niveau d’erreur ou au contraire de trop filter l’affichage de potentiels problèmes mineurs.

C’est la première ligne du fichier log4j.properties qui indique le niveau d’erreur minimum que je filtre, avec le niveau DEBUG, log4j, va afficher les logs FATAL,ERROR,WARM, INFO et DEBUG.

log4j.rootLogger=DEBUG // affiche   FATAL,ERROR,WARM, INFO et DEBUG
log4j.rootLogger=ERROR// affiche FATAL et ERROR

Je peux modifier le niveau de Log avec l’instruction suivante en cours d’exécution dans mon programme directement:

log.setLevel( org.apache.log4j.Level.INFO );

Ajouter un troisième logger en html en vous inspirant de ce code:

log4j.appender.html=org.apache.log4j.DailyRollingFileAppender
log4j.appender.html.DatePattern='.'yyyy-MM-dd-HH-mm
log4j.appender.html.layout=org.apache.log4j.HTMLLayout
log4j.appender.HTML.layout.LocationInfo=true
log4j.appender.HTML.layout.Title=Application logs
log4j.appender.html.File=C:/Dev/eclipse-workspace/application.html

Ce logger a plusieurs spécificités si vous lancer des logs en nombres. Consulter régulièrement le dossier où va être stocker vos logs et si vous utilisez Eclipse pour afficher le contenu de dossier, n’hésitez pas à faire rafraîchir le contenu du dossier.

Le premier niveau d’utilisation du Logger est de tout mettre dans un seul logger appelé ici « rootLogger » mais on peut aussi utiliser des catégories de logger pour séparer des sources de logs en fonction de différentes utilisations.

Supposons que l’on veut logger pour les réquêtes SQL de manière séparée, on peut créer un logger appelé « requete » avec ses propres réglages:

log4j.rootLogger=DEBUG,stdout, file
log4j.logger.requete=INFO, requete

log4j.appender.requete=org.apache.log4j.RollingFileAppender
log4j.appender.requete.File=c:/Developpement/requete.log log4j.appender.requete.layout=org.apache.log4j.PatternLayout 
log4j.appender.requete.layout.ConversionPattern=%-7p %d [%t] %c %x - %m%n
log4j.appender.requete.MaxFileSize=10KB
log4j.appender.requete.MaxBackupIndex=5

Dans ce cas, la fabrication de plusieurs fichiers requete.log.X sera réalisé automatiquement en préserver la taille d’un fichier au maximum à 10Kb puis au bout de 5 fichiers créé le plus ancien sera éffacé pour être recréé par un mécanisme de rotation.

5/ Installation d’un plugin Eclipse Log Viewer

Pour afficher les logs, j’utilise un plugin d’Eclipse qui s’appelle « Log Viewer« . Le but de cette partie en plus d’utiliser le Log Viewer est de voir l’ajout d’un plugin dans Eclipse ( et on a des tonnes de plugins dans Eclipse) et sont ajout dans une perspective d’Eclipse JEE.

Vous pouvez installer ce plugin en faisant  glisser-deposer le bouton « Install » dans Eclipse.

Comme ceci:

Confirmer l’acceptation de la licence, puis Accepter l’installation « Install anyway » sur la fenêtre « Security Warning »

Vous pouvez ensuite mettre dans votre pesrpective Eclipse ce nouveau plugin via le menu Windows > Show View > Other… et chercher Log Viewer

Dans la fenêtre Log Viewer, on peut ensuite ouvrir différents fichier de log, régler la coloration des logs, rafraichir l’affichage, etc.

En premier lieu, il faut ouvrir le fichier des logs créé avec le bouton Open Logfile:

log-viewer-open-file .

Ensuite, on peut indiquer les règles que l’on veut appliquer au Log Viewer via le bouton Show rules preferences:

log-viewer-rules

La liste des règles peut être modifiée pour mettre en surbrillance différentes lignes des logs selon un critère de recherche, dans notre cas s’il y a le mot INFO c’est en fond vert, si on a le mot ERROR, c’est en fond rouge:

Log-viewer-preferences

L’idée par exemple est d’avoir ce type de logger

@WebServlet(urlPatterns = { "/Controleur" })
public class Control extends HttpServlet {

  private Logger logger = Logger.getLogger("BoostrapSample.main.controleur");

Puis quelque part dans le code de la servlet, sur l’ajout d’un Todo:

// Action1 addTodo 
if ("addTodo".equals(action)) { 
   String texte = request.getParameter("todo_texte"); 
   Application.getInstance().addTodo(texte, false); 
   logger.debug( String.format("addTodo with parametres [%s,%s ] ", texte , actif )  );
   vueFinale = "index.jsp"; 
}

Dans l’absolut on pourrait remplacer l’appel aux paramètre texte et actif par un todo.toString() a surcharger dans la classe Todo.

6/ Classe Helper

Le Helper, est un design pattern, c’est une classe qui « aide une autre classe ». Prenons par exemple une classe Livre qui gère classiquement les informations d’un livre (on appelle cela un POJO: Plain-Old Java Object). Sa vocation est de stocker de l’information sur un Livre dans ses propriétés (titre, nombre de page, auteur, editeur, collection, isbn, etc.).

Prenons comme exemple, une ou plusieurs méhodes qui vérifient l’intégrité des données, plutôt que de les ajouter dans la classe Livre, on pourrait les ajouter dans une classe LivreHelper. On trouverai par exemple une méthode checkISBN() qui a vocation à vérifier le ISBN, checkAuteur() qui va vérifier que l’auteur existe vraiment dans une base de données en ligne, etc.

Toutes ces méthodes n’ont pas vraiment besoin d’être dans la classe Livre mais plutôt dans une classe annexe LivreHelper. De temps en temps dans un projet, on trouve par exemple une classe Util qui a des méthodes de convertion ou de vérification mais au final cette classe devrait être renomée et lié à un POJO car c’est une classe Helper.

Par exemple, nous allons ajouter une classe ControleurHelper qui va nous permettre d’aider le Controler en réalisant le log des paramètres passés au Controleur.

private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger( "BoostrapSample.main.ControlerHelper" );

public static void logAllParameters(HttpServletRequest request){

 Map<String, String[]> parameters = request.getParameterMap();
 
 for(String parameter : parameters.keySet()) { 
    String[] values = parameters.get(parameter);

    String value = Arrays.stream( values ).collect(Collectors.joining(","));

    log.info( "Parametre " + parameter + " " + value ); 
 }

}

ATTENTION: cette méthode utilise un fonctionnalité avancée du Java 8+ appelée stream. Quel intérêt au stream ? Le stream est une solution moderne pour exploiter les traitements multi coeurs des listes en Java. Elle permet de sérialiser des traitement en convertissant les anciens formats des différentes structures de listes (les classes/interfaces Set, List, ArrayList, Map, Array, Collection, etc.) dans un nouveau format plus rapide aux traitements.

La méthode suivante fait une convertion d’un type String[] values en stream pour permettre de réaliser des traitements plus performant et plus lisible avec les méthodes inlines.

Arrays.stream( values )

Nous allons donc faire appel à une méthode inline par exemple filtrer les informations.

NOTA: L’utilisation d’une méthode inline en Java se retrouve dans d’autres langage de programmation dont le javascript que l’on verra plus tard.

L’appel d’une méthode inline ou anonyme est une technique de programmation qui permet de créer une instance de classe avec une méthode et d’appeler tout cela en 1 ligne. Nous la retrouverons dans le code javascript notamment.

Par exemple, nous voulons une méthode qui appelle un évènement Action dans un interface graphique

Button b=new Button("Click Here"); 

b.addActionListener(
   new ActionListener(){ 
        public void actionPerformed(ActionEvent e){ 
            System.out.println("Hello World!"); 
    } 
});

On a la construction d’une classe ActionListerner pour faire appel à une seule méthode pour réaliser l’opération actionPerformed dans notre cas.

Voici la version inline que l’on appelle aussi lamba en Java:

b.addActionListener( e  -> System.out.println("Hello World!"));

On voit bien la simplification dans ce cas, on trouve parfois le terme de sucre syntaxique pour indiquer une facilité de compréhension dans le codage.

Donc avec ce sucre syntaxique, par exemple, si on souhaite traiter la liste des Todos comme un stream pour extraire le texte des Todos actifs uniquement.

Nous partons d’une liste de Todos que nous convertissons en stream

lesTodos.stream()

Maintenant nous allons utiliser une méthode filter pour n’utiliser que les Todos actif, nous pourrions passer une méthode qui donne la raison de la comparaison à la méthode filter. C’est l’opérateur -> qui indique à gauche quel est le nom paramètre sur chaque élément du stream que nous allons exploiter et à droite de l’opérateur l’action à réaliser.

todo -> todo.isActif()

est à mettre en parallèle à la création d’une classe anonyme avec une méthode qui est automatiquement appelé

Class anonyme{
    public static boolean méthode_appelé_automatiquement(Todo todo){
        return todo.isActif();
    }
}

Cela suppose un peu de code pour une méthode qui au final ne va pas être énormément exploitée. D’où l’utilisation des appels inline plus simple à écrire:

lesTodos.stream().filter( todo -> todo.isActif())

L’idée derrière les streams qui est utilisées dans le traitement de données de type fouille de données et Big Data et de pouvoir chaîner les opérations pour permettre à la JVM d’exploiter les multiples processeurs en parallélisant les actions.

Nous allons ensuite recupérer le résultat du filtrage et indiquer quel partie du Todo nous voulons extraire le texte du todo:

lesTodos.stream().filter( todo -> todo.isActif()).map( todo -> todo.getTexte() )

Nous pouvons maintenant exploiter la méthode collect pour fusionner les données en les contenants avec un séparateur et en transformant le résultat en String:

String reponse = lesTodos.stream().filter( todo -> todo.isActif()).map( todo -> todo.getTexte() ).collect(Collectors.joining(","));

Vous trouverez sur le web énormément d’exemple sur l’utilisation des streams qui sont en train de devenir très performant pour l’exploitation de données en parallèle (voir l’exemple de la somme d’entier en stream sur le site d’Oracle).

7/ Breakpoint conditionnel

Il me parait utile d’indiquer qu’Eclipse en Java permet de créer un point d’arrêt (le breakpoint en VO) en mode conditionnel. Vous pouvez ouvrir le « Breakpoint Properties » puis indiquer que ce breakpoint est « Conditional » et d’indiquer ensuite le critère qui déclenchera la pause sur le breakpoint, « Suspend when ‘true' » :

L’utilisation des breakpoints et le débogage est un outil très utile pour le développement. Autant parfois, je passe par toute les étapes de mon débugage pour reprendre l’enchainement des lignes de codes qui arrivent avant un point compliqué. Autant parfois, je peux indiquer un breakpoint et indiquer une condition qui pose un problème particulier.

A vous de tester et de voir ce qu’il vous parait le plus utile.

8/ Ajout de la partie Base de données

Pour exploiter la base de données, nous devons ajouter un connecteur JDBC pour que notre code Java puisse se connecter s’y connecter.

Si on a une base de données Mysql, il faut le connecteur Mysql. Ce connecteur s’appelle JConnector et il est disponible à cette adresse: https://dev.mysql.com/downloads/connector/j. Autant j’utilise régulièrement Mysql, autant je le trouve parfois un peu trop compliqué dans un TP pour gérer les droits sur les bases de données.

Nous allons exploiter par contre une base de données Sqlite. Pour cela, il faut ajouter le fichier sqlite-jdbc-3.8.11.2.jar accessible sur cette adresse (http://www.master-ctn.mines-ales.fr/install/M1-Web/commun/) et à mettre dans le dossier  WebContent\WEB-INF\lib\ avec le jar du log4j.

Pour utiliser sqlite, il faut une classe spécifique DaoManager qui va gérer les accès à la base de données. Vous pouvez vous inspirer de la classe DaoManager pour réaliser votre propre classe qui doit gérer la connexion et les opérations de manipulation de la base de données.

Au choix, le DaoManager peut être appelé par le Controleur ou par Application qui aura donc en charge la gestion des accès à la base de données.

Le fichier contenant la base de données s’appelle todo.db, ce dernier se trouve par défaut dans le dossier d’Eclipse. Il ne sera que quand il y aura des opérations sur les opérations sur la base de données.

Pour voir le contenu de la base de données sqlite,  on peut utiliser par exemple un outil DBrower for sqlite accessible sur ce site : https://sqlitebrowser.org/dl/.

NOTA: le fichier sqlite est verrouillé (VO: Lock) si vous l’ouvrez avec sqlitebrowser. Fermer le pendant les développements. Une ancienne version permettait d’ouvrir le fichier sans le verrouillermais je ne la retrouve plus.

L’installation est assez classique, il n’y a qu’un seul endroit où il faut cocher la « bonne option » :

Ensuite après avoir lancé DBBrowser for sqlite, ouvrer le fichier de base de données todo.db

Vous avez ensuite l’option de parcourir les données pour constater que tout les données sont bien présentes:

Nous pouvons faire beaucoup de manipulation avec cet outil mais ce n’est pas un TP de base de données. La base de données sqlite est une base de données très simple et assez répandue. Vous trouverez par exemple en ouvrant le fichier Main.db dans le jeu vidéo Blood Bowl que l’on peut ouvrir la base de données des équipes:

Il ne s’agit bien sur que d’un travail de rétro ingenieuring pour comprendre le fonctionnement du jeu, mais quelque part dans ces données, il y a un endroit qui stocke le nombre de pièce d’or des équipes. 😉

Plus concrètement, vérifier que votre application peut ajouter  des Todos dans la base de données et que vous pouvez recharger la liste des Todos depuis cette dernière. Actuellement, nous allons charger les todos à chaque appel sur le controleur. Il existe un mécanisme pour faire perdurer les données entre les multiples appel mais pour simplifier et pour être plus proche des techniques que l’on trouve en php/python/ASP, nous allons simplifier en rechargeant à chaque fois les données.

Maintenant le gros point de notre développement, il faut ensuite faire évoluer votre application en intégrant un identifiant numérique id produit automatiquement par la base de données.

Il faut ajouter une nouvelle propriété à notre classe Todo de type int.

Normalement, le DAOManager a créer une colonne auto incrémentée pour les Todos. La base de données génère donc l’information unique du numéro de Todo. Cette information est à retrouver avec une séries d’instructions qui commencent par un appel à la méthode getGeneratedKeys:

ResultSet rs = preparedStatment.getGeneratedKeys();
if (rs.next()) {
   autoGeneratedIdFromDatabase = rs.getInt(1);
   todo.setId( autoGeneratedIdFromDatabase );
} else {
 logger.error( String.format("Error exception %s ", ex.getMessage() ) );
}

Une fois le todo disposant de son Id il peut être inséré dans la liste des Todos du singleton Application.

Enfin, il faut bien sur recoder l’ensemble de l’application pour exploiter cet id dans la parti HTML et donc dans le Controleur avec mise à jours et effacer le ou les Todos.

Publié dans IMT Mines Alès | Commentaires fermés sur Tp2 IMT Mines Alès Développement Web

Tp1 IMT Mines Alès Développement Web

Aidez vous aussi de ces ressources, surtout le débugage, JSP to JSP, la vidéo d’installation (Eclipse/Tomcat) et de la vidéo de débugage

Vous le savez bien pour m’avoir vu l’utiliser souvent, le débogage est le moyen pour vous de trouver le bug qui bloque. Vous allez acquerir cette compétence en priorité tout comme quand vous m’appeliez en TP où je redémarrais le serveur Tomcat en débugage pour tracer l’appel entre le navigateur et la Servlet, c’est critique pour vous et pour ces TPs  😉 Documents Développement Web dont explication sur le débugage

 

0/ Utilisation plus « verte » des ressources de l’ordinateur

Je vous invite pour les TPs à réaliser ces actions pour consommer moins de ressources pour l’ensemble des TPs.

En premier lieu, je vous recommande de fermer toutes les applications non essentieles dont celle dans la barre systèmes.

En second lieu, je vous conseille de réduire la fréquence du processeur en mettant vos ordinateurs sur économie d’énergie au niveau de la gestion d’énergie. Il peut aussi être utile de réduire temporairement l’éclairage de l’écran à un niveau un peu plus faible mais confortable pour le travail.

En troisième lieu, sauvegarder de manière épisodique votre code pour éviter des recompilation systématique de votre code dans le menu « Project » > « Build Automatically » et à utiliser le raccourci Ctrl+B pour déclencher à la demande la recompilation du projet.

Enfin, nous limiterons le nombres d’onglets ouvert et de processus sur les navigateurs nottament sur le navigateur principal de travail.

 

1/ Description de l’application

Notre première application Web a pour objectif de gérer une liste de choses à faire par exemple une liste de courses (des todo):

Une règle métier défini par notre « client » est de ne pas avoir deux fois un « todo » avec le même texte par exemple impossible d’ajouter encore « chocolat » à la liste. Il n’y aura pas d’erreur mais l’élément ne sera pas ajouté une seconde fois.

Pour la partie métier/modèle qui est extrêmement simplifié pour permettre de se focaliser sur la partie Web.

Diagramme de classe simplifié :

Pour la gestion d’une seule et unique instance d’application, nous allons utiliser le Design Pattern Singleton:

public class Application {
   private static Application instance = null;
   
   public static Application getInstance() {
      if (instance == null) {
           instance = new Application ();
      }
      return instance;
     }

  private Application() {
    // initialisation de la liste 
  }

}

Comment instancier une application sachant que le constructeur est privé ?

Pourquoi on initialise la liste dans le constructeur ?

Nous allons utiliser l’interface List pour gérer la liste lesTodos qui seront de nature ArrayList<Todo>, l’interface List est dans la package java.util.List où on trouve uniquement la signature des méthodes et non pas le code associé à la méthode qui sera surchargé par ArrayList:

Ouvrer le code java du List et du ArrayList en faisant touche Ctrl + Clic sur List ou sur ArrayList.

Un interface est une liste de nom de méthode que doit avoir un classe instanciable pour être facilement remplaçable par une évolution. On pourra par exemple imaginer dans une évolution de notre application une exploitation d’une classe LinkedList ou un résultat d’un stream (technologie que l’on verra plus tard).

Les méthodes que l’on utilisera régulièrement de cet interface seront contains, get et indexOf par exemple. 

 

2/ Création du projet et de la partie métier

Faire un nouveau « Dynamic Web Project » ( File > New > Dynamic Web project ), n’oubliez pas d’indiquer sur quel serveur tomcat (target runtime) il faut déployer votre projet sinon vous devrez faire sur le serveur Tomcat (dans l’onglet server) clic droit > « Add and Remove… » pour ajouter votre projet au Tomcat:

Préparer des packages dans le projet créé, par exemple codes de la partie métiers ne doivent pas être directement dans le dossier « Java Ressources > src » mais dans un package metier ou model (clic droit sur le dossier src > New > Package)

Préparez aussi des packages controleur et test pour la suite des TPs.

Codez les deux classes du diagramme UML ci-dessus (Application et Todo) et les méthodes nécessaires au fonctionnement (clic droit sur le package metier > New > Class)

Vous pouvez vous aider de la génération des méthodes get et set avec clic droit sur le code de la classe > Source > Generate Getters and Setters…

Profitez-en pour générer les méthodes hashCode() et equals() dans le même menu, en utilisant le champ « texte ». Creer aussi 2 constructeurs, un avec tous les champs et un sans aucun champ via le Clic droit « Source > Generate Constructor using Fields ».
Essayer de comprendre à quoi peuvent servir la méthode equals en utilisant par exemple ce genre de code:

Todo todo1 = new Todo( "lait", false );
Todo todo2 = new Todo( "beurre", true);

if ( todo1.equals( todo2 ) {
    System.out.println(" lait equals beurre ");
}

todo2.setText( "lait" );
if ( todo1.equals( todo2 ) { 
    System.out.println(" lait equals lait ");
}

Créer dans Application, une méthode main pour exécution en mode Java classique et utiliser cette méthode pour créer une application : 

public static void main(String[] args){
  Application.getInstance().addTodo("lait");
Application.getInstance().addTodo("beurre"); }

Coder les méthodes de la classe Application qui permettent de manipuler la liste lesTodos et exécuter via Clic droit sur le code de l’Application puis choisir Executer via « Run as » > « Java application ». 

Des méthodes additionnelles sont à réaliser, le diagramme de classe est simplifié mais on peut imaginer par exemple des méthodes « findTodo« .

3/ Tests unitaires avec Junit 4

Nous allons produire différents tests unitaires pour vérifier que les classes suivent quelques règles.

Sur le package test, faire clic droit > New > Other > Junit Test Case puis remplir l’interface comme la capture d’écran ci-dessous :

J’attire votre attention sur le dernier champ de cet interface « Class under test », il faut bien indiquer la classe dont vous voulez coder le test unitaire. J’indique donc  que je veux dans le dossier src de mon application, dans le package test créer une classe de test TodoTest qui va tester la classe metier.Todo puis faire « Next ».

Dans la fenêtre suivante, j’indique la liste des méthodes que je souhaite tester:

Dans notre cas, on souhaite tester les méthodes de la classe Todo qui sont sélectionné. Il peut paraître peut utile de tester les classes parentes comme Object.

Voici un extrait en exemple de code pour le test unitaire de la classe Todo.
Essayez de coder quelques tests pertinents et exécutez les via clic droit sur le projet > Run As > Junit Test

public class TodoTest {

  @Test
  public final void testTodo() {
     Todo todo = new Todo("du lait" , false);
     assertNotNull( todo );
  }

@Test
public final void testGetTexte() {
   Todo todo = new Todo("du lait" , false);
   assertEquals ( todo.getTexte() , "du lait");
}

@Test
public final void testIsActif() {
   Todo todo = new Todo("du lait" , false);
   assertFalse( todo.isActif());
}

}

Autant les tests sur la classe Todo (un POJO) sont très basiques autant les tests unitaires sur la classe Application sont plus pertinent.

On peut ainsi tester de manière plus avancé :

  • que le Singleton n’est jamais null,
  • que 2 applications sont identiques
  • que la liste des Todo est vide au démarrage,
  • que l’ajout d’un Todo nous permet ensuite de le retrouver,
  • que l’ajout d’un Todo avec le même texte ne change pas longeur de la liste
  • qu’ajouter un Todo se fait en fin de liste
  • qu’effacer un Todo ne permet pas de le retrouver

Coder ces tests unitaires pour vérifier que la partie métier fonctionne correctement.

A chaque fois que vous développerez une méthode importante, pensez à coder le test unitaire et à relancer la batterie de test pour vérifier la non régression de votre application.

Refaite une classe de Test sur Application en cochant les méthodes: setUpBeforeClass, tearDownAfterClass, setUp, tearDown:

Coder dans chacune de ces méthodes un affichage d’information. Par exemple pour tearDown:

System.out.println( "teardDown" );

Mettez un point d’arrêt sur TearDown et lancer le test en mode Debug, via Clic droit sur le code source > Debug As > Junit Test

A quoi peut bien servir ces 4 méthodes: setUpBeforeClass, tearDownAfterClass, setUp, tearDown ?

4/ Liaison entre la partie métier et une JSP

Faite une JSP de test pour l’affichage de 4 todos :


Voici un exemple de code pour créer Application avec des données et éviter une liste vide (noter que la construction de la liste est faite dans le constructeur) :

private Application() {
     lesTodos = new ArrayList<Todo>();
     create4TestTodos();
 }
 private void create4TestTodos(){
     addTodo( "chocolat" , false );
     addTodo( "bonbons" , false );
     addTodo( "Chocolat actif" , true );
     addTodo( "Bonbons actif" , true );
 }

Créer une JSP et importer les classes métier dans cette dernière, habituellement en tête de page:

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="fr.ema.lgi2p.emacs.metier.*" %>
Parcours de la liste des Todos de type « foreach » :
<%
for(Todo todo : Application.getInstance().getLesTodos() ){
%>
<p>
   <%=todo.getTexte() %>
</p>
<%
}
%>

Cet affichage est statique, cela nous permet juste d’afficher depuis la partie Métier les données dans une JSP qui symbolise la partie Vue.

 

5/ La Servlet comme partie Controleur

Notre contrôleur va utiliser la technologie de Servet pour recevoir les données (Menu clic droit > New > Servlet ). La servlet devra être dans un package controleur.

Le mapping est très utile pour lier une URL /Controleur à la classe Controleur. On peut faire des mapping plug avancé de style *.pdf vers une servlet pour vérifier que toute demande de PDF passe par la Servlet.

Nous allons créer une Servlet de la classe Controleur qui répond à une liste d’URL, dans notre cas /Controleur.

L’URL Mapping permet de faire la liaison entre l’URL http://127.0.0.1:8080/NomProjet/Controleur et la servlet appelé.

Développez votre contrôleur en vous inspirant de cet exemple :

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

String vueFinale = "index.jsp";

// TODO: Actions du controleur pour calculer la vueFinale, 
// faire un appel avec la méthode doAction ci-dessous à vous de code maintenant
     
request.getRequestDispatcher(vueFinale).forward(request, response);

}

Le but de la variable « vueFinale » est de contenir le nom de la JSP qui va être appelé par la commande getRequestDispatcher.

Si une opération, par exemple addTodo ne fonctionne pas, alors la vueFinale sera par exemple « erreur.jsp » et c’est cette vue (JSP) qui sera affichée alors.

Dans le contrôleur, nous allons coder une méthode doAction qui va prendre des paramètres de la requête HTTP Get ou Post et notamment un paramètre action qui va permettre d’appeler les méthodes des la partie Modèle et de fabriquer cette fameuse vueFinale:

private String doAction(HttpServletRequest request) {

String vueFinale = "erreur.jsp";

String action = request.getParameter("action");

// Action1 addTodo
if ("addTodo".equals(action)) {

    String texte = request.getParameter("todo_texte");
    Application.getInstance().addTodo(texte, false);

    vueFinale = "index.jsp";
}

// Action2 Update list ...


// ... 
return vueFinale;
}

Donc l’appel d’une URL /Controleur?action=addTodo&todo_texte=bonbons  doit permettre d’appeler le Controleur d’ajouter le nouveau Todo dans le singleton Application et puis ensuite Dispatch vers la vueFinale.

Nota Controleur

Pour accéder à la liste des nom de paramètres des champs de formulaires, pour rappel dans l’URL par exemple http://127.0.0.1:8080/Control?nom=jean&prenom=pierre&civilite=Mr
si on veut obtenir la liste [« nom », »prenom », »civilite »], on peut exploiter cette opération:

List<String> keys = Collections.list(request.getParameterNames());

Fin du Nota, retour au TP…

Développer une JSP pour présenter cette vue avec une liste et un formulaire pour ajouter un nouveau Todo (la barre horizontale se fait avec le tag <hr/>):

NOTA: une checkbox dans un formulaire n’est envoyée que si elle est cochée.

Voyez ce diagramme explicatif qui montre l’envoie de données entre le formulaire en HTML, son affichage dans le navigateur avec sélection par l’internaute et l’envoie à la servlet via le bouton submit du formulaire qui fabrique une URL:

N’hésitez pas à afficher cette image en plein écran et en zoomant avec votre navigateur pour voir les liens entre les différents éléments.

Ajouter un formulaire pour mettre à jours la ou les cases (checkbox) sélectionnées.

Les listes en HTML se basent sur les tags suivants:

<ul>
<li>chocolat</li>
<li>bonbons</li>
</ul>

Donc nous allons lier le formulaire avec ces tags de listes:

<form method="get" action="./Controleur">
<ul>
<li><input type="checkbox" name="checkbox_chocolat" /> chocolat</li>
<li><input type="checkbox" name="checkbox_bonbons" /> bonbons</li>
</ul>
<!-- ici il manque quelque chose pour soumettre (en VO: submit le formulaire -->
</form>

Pour que le champ de formulaire <input type= »checkbox » /> soit cochée si le Todo correspondant a comme propriété actif à la valeur true.il faut que l’on ai un attribut checked dans le tag comme ceci:

<input type="checkbox" name="checkbox_chocolat" checked />

Pour que la case soit non cochée, rien comme ceci:

<input type="checkbox" name="checkbox_chocolat"  />

Bien sur on pourrait coder comme ceci:

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="fr.ema.lgi2p.emacs.metier.*" %>
Parcours de la liste des Todos de type « foreach » :
<%
for(Todo todo : Application.getInstance().getLesTodos() ){
%>
<p>
   <%if ( todo.isActif() { %>
      <input type="checkbox" name="checkbox_" checked />
    <% } else { %>
       <input type="checkbox" name="checkbox_" />
    <% } %>
</p>
<%
}
%>

Uniquement après que vous ayez essayer vous-même:

Cliquez ici pour afficher une proposition de solution pour gérer checked:

6/ Actions du controleur: Update Todo

Habituellement, je laisse les élèves coder comme ils le souhaitent cette partie du cours/TP.

Donc je vous invite à ne pas regarder cette solution et à la coder vous même.

J’ai 2 startégies classiques sur la partie HTML:

  • La première est de continuer avec un formulaire pour la liste des Todo et un second formulaire pour l’ajout d’un todo
<form method="get" action="./Controleur">
<ul>
  <li><input type="checkbox" name="checkbox_chocolat" /> chocolat 

   </li>
<li><input type="checkbox" name="checkbox_bonbons" /> bonbons</li>
</ul>

 <input type="submit" name="action" value="Update list" /> 
</form>

<form method="get" action="./Controleur">
New Todo: <input type="text" name="todo_texte" />
 <input type="submit" name="action" value="Add todo" /> 

</form>
  • La seconde solution est tout aussi valable en utilisant un formulaire par ligne:
<form method="get" action="./Controleur">
<ul>
  <li><input type="checkbox" name="checkbox_chocolat" /> chocolat 
 <input type="submit" name="action" value="Update liste" /> 
   </li>
</ul>
</form>

<form method="get" action="./Controleur"> 
<ul>
  <li><input type="checkbox" name="checkbox_bonbons" /> bonbons
<input type="submit" name="action" value="Update liste" /> 
</li>
</ul>
</form>
</form>

<form method="get" action="./Controleur">
New Todo: <input type="text" name="todo_texte" />
 <input type="submit" name="action" value="Add todo" /> 

</form>

Les 2 solutions sont valides, après la seconde est plus simple quand on veut ajouter un second bouton « Delete Todo » alors que la première doit obliger le controleur à passer sur toute les checkbox envoyées.

Pour la récupération des données côté servlet, pour l’action Update list, je vous recommande de décocher tous les Todos de votre liste application puis de mettre à jours la liste des todos en trouvant ceux qui ont été envoyé par le formulaire.

Cela pourrait ressembler à ceci côté Servlet Controleur

if ( "Update list".equals( action ) ){
    // TODO: nouvelle méthode dans Application
    Application.getInstance().uncheckAll();
    List<String> keys  = Collections.list(request.getParameterNames());		
    for(String key: keys) {		
	if ( key.startsWith("checkbox_")) {
          String todoTexte = key.replace("checkbox_", "");
	  Application.getInstance().updateTodo(todoTexte, true);				
	} 
     }
}

Bien sur pour améliorer notre code, on pourrait avoir l’exploitation de ces opérations dans une méthode se trouvant dans Application:

if ( "Update list".equals( action ) ){
    // TODO: nouvelle méthode dans Application qui appelera aussi uncheckAll() par exemple
    Application.getInstance().updateListTodo( Collections.list(request.getParameterNames() );
}

Nota: une autre option générale est d’exploiter le fait que l’on peut créer un formulaire comme ceci:

<form method="get" action="./Controleur">
<ul>
  <li><input type="checkbox" name="todo" value="chocolat" /> chocolat 

   </li>
<li><input type="checkbox" name="todo" value="bonbons" /> bonbons</li>
</ul>

 <input type="submit" name="action" value="Update list" /> 
</form>

L’URL constituée si les 2 cases sont cochées, sera alors http://127.0.0.1:8081/Controleur&todo=chocolat&todo=bonbons&action=Update list

La récupération de la liste des valeurs du champ todo est faite comme cela:

 String[] valuesCheckboxTodo = request.getParameterValues("todo");

Cette option n’est pas forcément la meilleure car rapidement je prefererai avoir des checkbox avec l’identifiant unique de chaque todo:

<form method="get" action="./Controleur">
<ul>
  <li><input type="checkbox" name="checkbox_456" /> chocolat 

   </li>
<li><input type="checkbox" name="checkbox_987" /> bonbons</li>
</ul>

 <input type="submit" name="action" value="Update list" /> 
</form>

NOTA: Pour retirer de la console de debuggage dans le navigateur l’erreur de l’appel au favicon.ico, vous pouvez dans la partie <head> de votre JSP ajouter ce code :

<link rel="icon" href="data:,">

Sinon il faut rajouter un appel au favicon.

POINT D’ETAPE, LIVRAISON INTERMEDIAIRE

Livraison du code intermédiaire sur cette URL, je souhaite dans un fichier zip de type prenom.nom.zip tous les fichiers se  trouvant dans le dossier eclipse-workspace-2023\DevWeb\src\ (à modifier selon l’endroit ou se trouve votre Workspace.

Les fichiers sont à remettre sur campus sur cette adresse:

https://campus2.mines-ales.fr/mod/assign/view.php?id=26510

 

 

7/ Optionnel: Ajout d’une nouvelle action xml et json

Si le paramètre « action » de l’URL contient le mot xml, il faut alors appeler une nouvelle JSP qui va fabriquer un fichier XML qui aura cette forme:

<?xml version="1.0" encoding="utf-8"?>
<todos>
<todo id="1" texte="chocolat" actif="false" />
<todo id="2" texte="bonbons" actif="true" />
<todo id="3" texte="lait" actif="false" />
</todos>

Bien sur ces données doivent être extraites de la liste des Todos de la partie Modèle.

Si le paramètre « action » de l’URL contient le mot json, il faut extraire les données dans un autre format, le format JSON qui est une autre façon moins verbeuse de présenter de l’information:

{
 "todos":
 [
    {
      "id": "1",
      "texte": "chocolat",
      "actif": "false"
    },
    {
       "id": "2",
       "texte": "bonbons",
       "actif": "true"
    },
    {
      "id": "3",
      "texte": "lait",
      "actif": "false"
    }
  ]
}

Ce format est prévu pour un échange entre le code javascript et le serveur web et non pour être consulté par des internautes.

Vous pouvez vérifier que votre fichier est JSOn via un visualisteur en ligne tel que: Online JSON Viewer.

8/ Optionnel: client pour fichier JSON type restfull

Vous pouvez, si vous le souhaiter coder un client Java (dit client lourd par opposition au Web client léger) qui va accéder de la donnée via JSON en se comportant comme un client. Ce programme est un projet Java classique séparé du développement Web mais qui appelle le serveur Web Tomcat. C’est l’équivalent du client OneDrive/DropBox/GoogleDrive/etc. installé en local sur un ordinateur pour accéder à des données via l’API. 

On parle par exemple de commande wget ou curl mais Java est capable de communiquer aussi avec un serveur web via une requête GET simulée dans une classe classique. On peut imaginer ce code par exemple dans une application Android ou par une application installé sur l’ordinateur des utilisateurs (Dropbox a une version Web, Android et Desktop de son application).

Il y a plusieurs bibliothèques qui permettent de convertir directement un appel à un json en instance de classes (Google Gson, Jackson, Json-Lib). Pour plus de simpliciter dans l’installation et pour voir un peu « sous le capot », nous allons utiliser jdk.nashorn inclus dans le JDK 1.8 et exploiter l’analyser de script javax.script.ScriptEngine.

Par étape (manque la gestion des exceptions à coder):

// Création d'une URL pour récuper les données JSON

HttpURLConnection urlConnection = null;
try {
  URL url = new URL("http://127.0.0.1:8081/HelloWorld/json.jsp");
  urlConnection = (HttpURLConnection) url.openConnection();
} catch (MalformedURLException e) {
    e.printStackTrace();
} catch (IOException e) {
   e.printStackTrace();
}

Récupération des données JSON envoyés depuis le serveur web

 StringBuilder stringBuilder = new StringBuilder();
if (urlConnection != null) {
  try (BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()))) { 
    String line;
    while ((line = reader.readLine()) != null) {
       stringBuilder.append(line);
    }
  } catch (IOException e) {
      e.printStackTrace();
   }
}

Extractions du JSON en instance parsable via les classes ScriptEngine, ScriptObjectMirror et leurs méthodes d’extractions d’informations:

String script = "Java.asJSONCompatible(" + new String(stringBuilder) + ")";
ScriptEngine engine = new ScriptEngineManager().getEngineByName("javascript");
try { 
   ScriptObjectMirror result = (ScriptObjectMirror) engine.eval(script);
   for(Object objectScriptObjectMirror : (JSONListAdapter) result.get("todos")) {
      ScriptObjectMirror elementScriptObjectMirror = (ScriptObjectMirror)objectScriptObjectMirror;
      String texte = (String) elementScriptObjectMirror.get("texte");
      String actif = (String) elementScriptObjectMirror.get("actif");

       Todo newTodo = new Todo();
       newTodo.setTexte(texte);
       newTodo.setActif( "true".equals(actif));
     }
} catch (ScriptException e) {
    e.printStackTrace();
}

A mettre dans un test unitaire par exemple, un test de haut niveau bien sûr…

 

9/ Optionnel : utilisation d’un Tag JSP

Attention, il y a une utilisation d’un nouveau langage de template appelé ‘Expression Language’ ou EL. Cette notation exploite une version simplifié de l’appel aux getters et setters de type ${todo.texte} mais fait l’appel au code de getTexte().

Supposons que l’on souhaite extraire la partie génération HTML d’un élément de liste avec un todo et sa checkbox. On peut créer dans le nouveau dossier WebContent/WEB-INF/tags/, un nouveau fichier de type « JSP tag »

Appelons ce fichier todo.tag avec ce contenu

<%@tag language="java" pageEncoding="ISO-8859-1"%>
<%@tag import="main.model.Todo" %>
<%@attribute name="todo" required="true" type="main.model.Todo"  %>

<li>
	<input type="checkbox" name="todo_${todo.texte}" value="${todo.texte}"/> 
${todo.texte}   
		
</li>

On indique qu’il faut obtenir un attribut todo de type main.model.Todo dont on réalisé l’importation dans cette JSP Tag

Côté page JSP classique qui va utiliser cette JSP Tag, il faut en premier lieu l’importer

<%@taglib prefix="projetDevWeb" tagdir="/WEB-INF/tags" %>

On indique un préfixe pour éviter les conflits de nom avec d’autres JSP Tag.

Pour l’utilisation il suffit d’appeler la JSP Page en lui passant l’attribut todo demandé par la JSP Tag

<projetDevWeb:todo todo="${currentTodo}" > </projetDevWeb:todo>

Alors attention la variable currentTodo doit être explicitement sauvegardé dans le contexte de la page (ou de la session ou de l’application selon son besoin) car le contexte d’éxécution de la JSP tag est différente du contexte de la JSP. Les variables créées par la page JSP ne sont pas automatiquement visible par la JSP Tag. Pour rendre partageable cette variable il faut l’indiquer avec un contexte :

pageContext.setAttribute("currentTodo", todo , PageContext.PAGE_SCOPE);

On obtient donc ce code complet

<%
for(Todo todo : Application.getInstance().getLesTodos()){	
	pageContext.setAttribute("currentTodo", todo , PageContext.PAGE_SCOPE);		
%> 
	<projetDevWeb:todo todo="${currentTodo}" >
	</projetDevWeb:todo>
<%
}
%>

Il est aussi à noter que le langage appelé EL utilisant la notation ${instance.propriété} permet l’appel simplifié aux getters et aux setters. Cette version simplifiée de l’utilisation de taglib et de la JSTL est vraiment basique pour diviser le rendu d’une page en plusieurs sous éléments.

Une dernière option est d’exploiter le langage EL tout le temps en ajoutant les JARs suivants: jakarta.servlet.jsp.jstl-1.2.6.jar et jakarta.servlet.jsp.jstl-api-1.2.7.jar pour Tomcat 9.

Le code s’appuie sur une nouvelle bibliothèque de tag par défaut à ajouter en début :

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

Puis de réaliser la boucle avec le <c:forEach>

<c:forEach 
	items="${Application.getInstance().getLesTodos()}" 
	var="todo" >  	
    	<projetDevWeb:todo todo="${todo}" > </projetDevWeb:todo>    
</c:forEach>

L’exploitation du singleton Application nous permet de disposer de la variable dans tout le contexte de la page et du Tag JSP.

Publié dans IMT Mines Alès | Commentaires fermés sur Tp1 IMT Mines Alès Développement Web

Master m-Rehab

Installation des logiciels :

  • R Studio : https://posit.co/download/rstudio-desktop/
  • Weka (avec Java) : https://waikato.github.io/weka-wiki/downloading_weka/
  • Mysql dans Wamp pour Windows :
    • Mysql installation dans Wamp Server (wampserver2.5-Apache-2.4.9-Mysql-5.6.17-php5.5.12-64b.exe) :

https://sourceforge.net/projects/wampserver/files/WampServer%202/Wampserver%202.5/

Windows (x86, 64-bit), ZIP Archive puis ne pas créer de compte juste « No thanks, just start my download. » attention d’aller chercher aussi ces fichiers :  Visual Studio 2012 : VC 11 vcredist_x64/86.exe et Visual Studio 2012 VC 11 vcredist_x64/86.exe : http://www.microsoft.com/en-us/download/details.aspx?id=30679

Sur MacOS : https://www.mamp.info/en/downloads/


Cliquer pour télécharger la présentation du cours

Publié dans Médecine | Laisser un commentaire

TP IMT Mines Ales 0

Les cours de technologie « Web SIA-IL-8.5 Mobilité et multimédia » est disponible en téléchargement Web-Development avec des explications détaillés sur le développement Servlet/JSP en MVC.

Les outils de développement (Eclipse J2EE et Tomcat 10) sont disponibles selon l’OS de votre poste à cette adresse http://www.master-ctn.mines-ales.fr/install/M1-Web/. Ce sont des anciennes versions mais vu le peu d’évolution des deux logiciels, il n’y a pas beaucoup de différence avec les versions actuelles.

Je ne peux que vous recommander l’utilisation des outils de développement de Chrome (CTRL+MAJ+I) ou de Firefox (CTRL+MAJ+I) surtout l’onglet « Réseau/Network » et le clic droit sur une page web > « Inspecter » et « Source de la page/Code source de la page ». En fin de cette page, vous trouverez un court TP pour l’utilisation de ces outils.

Nota bene : une vidéo explicative se trouve en fin d’article, à vous de voir entre la version texte et la version vidéo.


TP 0 Configuration et installation de Tomcat avec Eclipse J2EE  à adapter selon votre OS et votre configuration personnelle

  1. Installation d’un JDK récent en version Oracle : télécharge par exemple JDK 11 (anciennement, j’utilisais JDK 8u161 ). Notez le dossier où se trouve votre JDK, attention en cas de mises à jour du JDK, le dossier peut changer…
    NOTA : Si vous avez déjà installé le JDK mais sans savoir où il pourrait être sur votre disque dur, cherchez le logiciel « javac » c’est le compilateur java, il se trouve dans le sous-dossier « bin »  de votre JDK
    NOTA2 : OpenJDK en développement pose quelques petits problèmes avec des warnings ou des erreurs peu explicitent, je ne souhaite pas l’utiliser pour l’instant en développement.
  2. Installation d’Eclipse J2EE depuis http://www.master-ctn.mines-ales.fr/install/M1-Web/  ou depuis (https://www.eclipse.org/downloads/packages/) dans un dossier dont le chemin ne comporte pas d’espace et d’accents par exemple dans un dossier Developpement ou DEV.
    Attention : nous utilisons « Eclipse IDE for Java EE Developers » pas une autre version d’Eclipse, si vous avez une autre installation d’Eclipse elle peut ne pas contenir ce que nous avons besoin. NOTA: Windows 7/8/10 contrôle de manière très précautionneuse les fichiers se trouvant dans les dossiers Programmes, Program Files (x86), Windows et tout dossier à la racine du disque C. Il est préférable d’avoir un dossier Developpement à la racine du disque dur dans lequel on place les outils de développement.
  3. Démarrer Eclipse J2EE pour vérifier que le JDK a bien été trouvé car Eclipse utilise Java pour s’exécuter. A l’exécution d’Eclipse, un workspace est demandé pour stocker tous les configurations d’Eclipse et les projets Java Web. Merci de faire attention que le chemin de ce workspace ne contienne pas d’espace ou d’accents. Je vous conseille par exemple c:/Developpement/workspace_j2ee par exemple.NOTA: si vous voulez changer de workspace, faire dans Eclipse menu « File » > « Switch Workspace »
  4. Téléchargement d’une version de Tomcat 10.1.5 (depuis le même serveur http://www.master-ctn.mines-ales.fr/install/M1-Web/commun/) et la dézipper dans votre dossier Developpement par exemple dans C:/Developpement/apache-tomcat-10.1.5/. Attention à ne pas avoir tomcat dans un sous dossier d’apache-tomcat-10.1.5/apache-tomcat-10.1.5/  ou alors modifier les réglages en correspondances.
  5. Configuration d’un JDK par défaut et non d’un JRE. Eclipse J2EE utilise un Java JDK ou JRE pour s’exécuter. Il peut gérer aussi plusieurs JRE/JDK selon vos besoins de configuration. Pour simplifier nos développements, nous allons indiquer un unique JDK auprès d’Eclipse pour éviter les conflits et les erreurs.
    1. Menu « Windows » > « Preferences » > « Java » > « Installed JREs »
    2. Bouton Add pour ajouter une  » Standard VM « dont le JRE Home et le dossier localisé dans l’étape 1 avec l’installation du JDK.
    3. Rendre le JDK par défaut et effacer l’ancien JREeclipse_preference_jre
      Un seul JDK ici en version jdk1.7.0_17 pour éviter les problèmes d’en avoir plusieurs et de ne plus savoir lequel est activé par défaut. Eclipse permet de jongler entre plusieurs perspectives qui sont affichées en haut à droite dans une sorte d’onglet. Metter la version installé précédemment pour le TP.
  6. Pour ouvrir une nouvelle perspective, faire
    Menu « Window » > « Open Perspective » > « Other »
    Les perspectives que l’on va utiliser sont Java EE et Debug, il faut afficher la perspective Java J2EE et dans cette perspective, activer l’onglet server (voir l’image)eclipse_perspective-server
  7. Menu File > New > Other > Server > Server > Next > Apache > Tomcat v10.1 Server. Il faut indiquer le  » Tomcat Installation Directory » c:/Developpement/apache-tomcat-10.1.15/ puis Finish. Le JRE à indiquer est celui qui a été configuré à l’étape 6 (il s’agit donc un JDK et non un JRE)
  8. Dans l’onglet « Server  » faire clic droit sur le serveur nouvellement créé, puis choisir Debugeclipse-debug-server
  9.  L’onglet « Console  » doit afficher les traces du serveur Tomcat. Ouvrez un navigateur et indiquez cette URL (Le port par défaut de tomcat est 8080) http://127.0.0.1:8080/
  10. Dans Eclipse, faire un nouveau projet File > New > Other > Web > Dynamic Web Project
  11. Il faut indiquer un nom de projet par exemple  » DevWeb », pour l’option Configuration on peut aussi choisir « Minimal Configuration » puis le bouton Finish
  12. Il faut ensuite placer une image PNG ou JPG dans le dossier « webapp » (historiquement « WebContent » ) du projet DevWeb
  13. Trouver l’URL pour accéder à cette image par le navigateur puis déplacer cette image dans un sous dossier img dans « webapp » (historiquement « WebContent » ). Cette URL devrait être de la forme http://127.0.0.1:8080/DevWeb/<nom_image.png>
  14. Ensuite, télécharger une page HTML et CSS par exemple depuis cet exemple du CSS Zen Garden. Simplement ce design n’ayant pas d’images, il suffit de cliquer sur : DOWNLOAD THE EXAMPLE HTML FILE AND CSS FILE pour récupérer le fichier html et le fichier css.
  15. Vous pouvez copier-coller le fichier Html et CSS depuis votre dossier téléchargement dans Eclipse en faisant glisser les fichiers dans le dossier « webapp » (historiquement « WebContent » ) .
  16. Vérifier que cette page s’affiche correctement en vérifiant les URLs via les outils de développement dans le navigateur : 
  17. Il faut fabriquer ensuite une page index.jsp dans le dossier « webapp » (historiquement « WebContent » ) avec le code ci-dessous par exemple :code-index.jsp
  18. Pour lancer le Debug, après avoir placé un point d’arrêt, il faut faire un clic droit sur ce code puis Debug As > Debug on Server ( on peut cocher « Always use this server when running this project » pour éviter cette étape):eclipse--clic-droit-debugeclipse-debug-choix-server
  19. Bouton Next pour ajouter le projet dans la liste à droite des projets déployés sur ce tomcat. Puis bouton « Finish » pour lancer le debuggage pas à pas.
  20. Pour plus de détail sur le Debug voir cet article spécifique pour comprendre ldé Debug pas à pas : (Eclipse débogage et points d’arrêts)
  21. Essayez d’inclure différents contenus comme des images et du Css avec la page jsp en mettant ces images et un fichier css dans des dossiers séparés dans le dossier « webapp » (historiquement « WebContent » ) .
  22. Définir le navigateur Chrome (ou sa version open-source Chromium) comme navigateur par défaut avec les options d’ouverture des outils de développement (et d’autres options) se fait via le Menu Windows > Preferences

    Les options que je propose sont les suivantes : –disable-web-security –disable-gpu –user-data-dir=c:\Developpement\eclipse-worskpace –auto-open-devtools-for-tabs –remote-debugging-port=9222 -incognito –single-process  « %URL »

     

    –disable-web-security : permet d’ouvrir 2 contenus provenant de 2 serveurs web différents, utile dans le cas d’un développement sur Framework Javascript utilisant nodejs en plus de tomcat
    –disable-gpu: permet d’éviter d’utiliser la carte GPU, parfois utile pour avoir un navigateur plus « basique », plus « généraliste »
    –user-data-dir : permet d’indiquer un dossier dans lequel on peut directement éditer depuis Chromium des fichiers CSS et Javascript
    –auto-open-devtools-for-tab: permet d’ouvrir par défaut les outils de développements web intégré dans Chrome
    –remote-debugging-port: utilisé pour le débogage de Javascript surtout utile avec TypeScript
    –incognito: permet d’utiliser l’onglet de navigation privé
    –single-process permet de limiter l’utilisation d’un seul processus pour chrome pour éviter l’utilisation par anticipation fait par chrome de plusieurs processus pour des actions avenir
    –renderer-process-limit=2 limite la consommation pour le processus chrome de rendu, il rend le navigateur un peu plus lent mais c’est compatible avec le TP

Voici la vidéo explicative de l’enchainement des actions pour le débugage avec Eclipse qui reprend les étapes vues ci-dessus:


TP 0 Vidéo configuration et installation de Tomcat avec Eclipse J2EE  à adapter selon votre OS et votre configuration personnelle

Test des onglets Inspecter et Réseau des outils de développements intégrés dans les navigateurs internet

Pour tester les outils de développements Web du navigateur, vous pouvez vous rendre sur cette page : http://showcase.richfaces.org/richfaces/component-sample.jsf?demo=commandButton&skin=blueSky . Au dessus du lien « View source » faire clic droit « Inspecter » pour ouvrir les outils de développement. Regardez le contenu du tag suivant:

<span id="out"></span>

On peut voir que le tag ne contient rien. Regarder aussi l’onglet Réseau/Network et vider la liste des appels qui ont construit cette page. Maintenant remplissez le champ « Name » et appuyez sur le bouton « Say Hello ». On peut constater qu’il y a un appel Ajax vers le serveur pour répondre avec un nouveau fragment d’HTML et que le tag précédent est rempli par un nouveau contenu que l’on voit effectivement dans le navigateur.

Problème de Tomcat crash en mémoire avec port 8080 bloqué

Ce message au redémarrage de Tomcat en mode debug ou normal indique qu’un logiciel utilise les ports numéro 8080 et 8005 tout comme Tomcat. Habituellement, il s’agit d’un exécution de Tomcat par la JVM via son programme javaw.exe (ou sur MacOS/Linux javaw).

Le crash de Tomcat en cours de développement d’un logiciel peut arriver de temps en temps. La solution est par exemple de chercher dans le gestionnaire des taches la liste des processus pour trouver ce javaw.exe (ou sur MacOS/Linux javaw).

Mais plusieurs outils dont Eclipse peuvent utiliser javaw.exe donc il faut demander dans la liste des colonnes la ligne de commande complète qui aura ce genre de forme et dont on identifie l’appel à tomcat vers la fin de la liste des paramètre:

C:\DEV20\Java\jdk-17.0.2\bin\javaw.exe -XX:+ShowCodeDetailsInExceptionMessages -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:51658 "-javaagent:C:\DEV20\Eclipse-ee-2021-12\configuration\org.eclipse.osgi\1034\0\.cp\lib\javaagent-shaded.jar" "-Dcatalina.base=C:\DEV20\eclipse-workspace-jee\.metadata\.plugins\org.eclipse.wst.server.core\tmp1" "-Dcatalina.home=C:\DEV20\apache-tomcat-9.0.59" "-Dwtp.deploy=C:\DEV20\eclipse-workspace-jee\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps" --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED -Dfile.encoding=Cp1252 -classpath "C:\DEV20\apache-tomcat-9.0.59\bin\bootstrap.jar;C:\DEV20\apache-tomcat-9.0.59\bin\commons-daemon.jar;C:\DEV20\apache-tomcat-9.0.59\bin\tomcat-juli.jar" org.apache.catalina.startup.Bootstrap start

Il faut donc trouver le PID du processus javaw.exe dont le paramètre contient le mot tomcat

Dans le powershell Windows on peut exécuter cette commande :

Stop-Process -Id $(get-wmiobject win32_process | where commandline -match tomcat | select-Object Handle -ExpandProperty Handle)

Pour la version MacOS et Linux, la commande est habituellement celle-ci:

 kill $(ps -ef | grep "tomcat"  | awk {'print$2'} )
Publié dans IMT Mines Alès | Commentaires fermés sur TP IMT Mines Ales 0

WordPress nouveau bloc version simple

La constitution d’un nouveau bloc permet de rapidement produire un contenu répétable pour inclure par exemple une signature automatiquement disponible ou même un short code.

Dans le dossier wp-content\plugins\, en premier lieu, il faut créer un dossier, par exemple block-twitter avec la liste des fichiers suivants :

  • block.asset.php
  • block.js
  • block.json
  • index.php

Le premier fichier block.asset.php est standard avec ce code :

<?php return
    array( 'dependencies' =>
        array(
            'wp-blocks',
            'wp-element',
            'wp-polyfill'
        ),
        'version' => '0.1'
    );

Le second fichier block.js contient le JavaScript pour présenter le contenu du bloc côté front et côté backoffice (côté éditeur). On note dans cette première version que le bloc ne fait que présenter un message côté backoffice légèrement différent de la version frontoffice :

( function ( blocks, element ) {
    var el = element.createElement;

    blocks.registerBlockType( 'block-twitter/basic', {
        edit: function () {
            return el( 'p', {}, 'Signature du Master CTN' );
        },
        save: function () {
            return el( 'p', {}, 'Suivez le Master CTN sur Twitter http://twitter.com/MasterCTN' );
        },
    } );
} )( window.wp.blocks, window.wp.element );

Par la suite, nous mettrons un short code pour déclencher l’appel à un programme :

( function ( blocks, element ) {
    var el = element.createElement;

    blocks.registerBlockType( 'block-twitter/basic', {
        edit: function () {
            return el( 'p', {}, '[twitter_short_code_plugin]' );
        },
        save: function () {
            return el( 'p', {}, '[twitter_short_code_plugin]' );
        },
    } );
} )( window.wp.blocks, window.wp.element );

Le troisième fichier block.json contient la description des informations du bloc notamment son titre, pour le chercher dans la liste des blocs noter la valeur de « name » qui a été utilisée dans le fichier block.js :

{
    "apiVersion": 2,
    "title": "Block Twitter",
    "name": "block-twitter/basic",
    "category": "layout",
    "icon": "universal-access-alt",
    "editorScript": "file:./block.js"
}

Le quatrième fichier est index.php qui permet de fabriquer un plugin dont l’activation dans la liste des plugins de WordPress permettra de lier notre nouveau bloc avec l’éditeur de WordPress :

<?php
/**
 * Plugin Name: Block Twitter
 */

function block_twitter_register_block() {
    register_block_type( __DIR__ );
}
add_action( 'init', 'block_twitter_register_block' );

Pour faire la gestion du short code, ce fichier contiendra ensuite la gestion du code appelé par ce short code, ici juste un simple message :

<?php
/**
 * Plugin Name: Block Twitter
 */


function twitter_short_code_plugin(){
    return "<p>Le compte Twitter</p>";
}

add_shortcode('twitter_short_code_plugin', 'twitter_short_code_plugin');


function block_twitter_register_block() {
    register_block_type( __DIR__ );
}
add_action( 'init', 'block_twitter_register_block' );

Après avoir activé le plugin dans le backoffice de WordPress :

Plugin avant activation pour ajouter le nouveau bloc

Nous pouvons disposer de ce bloc dans la liste des blocs disponibles pour l’utiliser :

Bloc Twitter disponible pour ajouter du contenu

Finalement, avec la solution de gestion du short code on obtient ce rendu dans l’éditeur :

Ajout du short code dans la liste des blocs de la page d’exemple

Et le rendu côté front office remplace le short code par le message prévu entouré en vers juste pour rendre la zone plus visible :

Rendu du short code sur la page d’exemple côté frontoffice

Maintenant, il faut essayer d’ajouter la fonctionnalité de rendu dans l’éditeur du backoffice de WordPress plutôt que d’avoir le short code.

En premier lieu, modifions le fichier block.js qui doit faire un appel au serveur pour afficher le résultat d’un appel « ServerSide » et non plus le message (l’ancien code est en commentaire avec // ):

        edit: function () {
            // return el( 'p', {}, '[twitter_short_code_plugin]' );
            return wp.element.createElement(
                wp.serverSideRender,
                {
                    block: 'block-twitter/editeur'
                }
            );


        },

Dans le fichier index.php il faut que la fonction block_twitter_register_block contiennent l’action à faire suite à cet appel « ServerSide » :

function block_twitter_register_block() {
    register_block_type( __DIR__ );
    register_block_type(
        'block-twitter/editeur',
        [
            'render_callback' => 'block_twitter_render_callback',
        ]
    );
}

On enregistre un appel à la fonction block_twitter_render_callback quand une URL de cette forme sera émise pour afficher le bloc dans l’éditeur http://<serveur web wordpress>/wp-json/wp/v2/block-renderer/block-twitter/editeur?context=edit&post_id=2&_locale=user .

La fonction block_twitter_render_callback est à ajouter dans le fichier index.php pour permettre quand un bloc est affiché d’avoir le rendu au lieu du shortcode :

function block_twitter_render_callback( $atts ) {
 
    return "<p>Compte TWITTER visible Editor</p>";
}
Rendu du bloc dans l’éditeur dans le back office de WordPress

On peut voir qu’au lieu du short code, le HTML prévu dans la fonction block_twitter_render_callback est affiché. Le passage en HTML affiche le short code pour bien rappeler le fonctionnement par défaut de bloc.

Publié dans CTN | Laisser un commentaire

Protégé : Accès serveur calcul CERIS

Cette publication est protégée par un mot de passe. Pour la voir, veuillez saisir votre mot de passe ci-dessous :

Publié dans Développement | Saisissez votre mot de passe pour accéder aux commentaires.

Ajout de Javadoc dans un programme Java

La constitution de la documentation dans un projet Java peut être simplement réalisé directement dans le code source en utilisant un formatage spécifique dans un commentaire.

Le système Javadoc existe depuis la création de Java (https://en.wikipedia.org/wiki/Javadoc) et invite le développeur à produire directement dans son programme la documentation.

En premier lieu, on peut documenter le package si un fichier package-info.java est disponible. Je vous renvoie vers cette documentation qui explique l’utilité de ce fichier: https://www.baeldung.com/java-package-info. Dans le cas d’un petit projet informatique, voici un commentaire explique l’utilisation d’un package:

/**
 * main est le package principal du projet de test d'intégration de Jenkins
 */
package main;

Pour faire la suite, nous allons documenter une classe Main dans le fichier main.java :

/**
 * Classe de test pour l'intégration de Jenkins
 * @author Pierre Jean
 * @version 1.0
 */
public class Main {

Il faut être humble dans la constitution de la documentation. À mon avis, il vaut mieux produire la documentation progressivement en mettant les premiers éléments puis progressivement compléter cette documentation par ajout de détails.

L’étape suivante est l’ajout de la documentation d’une méthode basique.

	/**
	 * Méthode pour afficher le double de ce nombre 
	 * <p>
	 * Cette méthode sert pour tester l'intégration l'affichage d'un nombre. 
	 * Ce nombre est calculé en appelant une méthode d'une classe Calcul
	 * qui retourne le double du nombre.
	 * <p>
	 * Cette méthode permet de réaliser un test unitaire avec Jenkins.
	 *
	 * @param  nombre paramètre du nombre pour réaliser le doublement
	 * @return retourne le nombre affiché 
	 */
	public int afficheDeuxFois(int nombre) {
		System.out.println(Calcul.doublement(nombre));
		return Calcul.doublement(nombre);
	}

J’ai ajouté beaucoup d’explication sur une méthode très basique pour détailler la forme de cette documentation avec un seul paramètre et une valeur de retour.

Nous pouvons maintenant imaginer la génération de la documentation sous la forme de pages HTML via le Menu Project > Generate Javadoc …

Menu Generate Javadoc

Puis dans l’interface suivante, on indique sur quel projet (ou quels projets) on souhaite générer la Javadoc et le dossier de génération de cette documentation.

Projet et dossier de la génération de la documentation

La génération de la documentation va fabriquer différents fichiers HTML dans le dossier doc visible dans Eclipse. On doit donc indiquer si on souhaite réécrire dans le dossier en remplaçant l’ancien contenu.

Mises à jour par remplacement des dossiers

La fabrication du contenu est visible dans Eclipse dans le dossier doc indiqué dans les étapes précédentes. Il suffit ensuite d’ouvrir le fichier index.html via le menu « Open With » > « Web Browser » pour afficher la documentation finale.

Le rendu final de la documentation est alors affiché dans une forme standardisé réalisant les liens entre les différentes pages de la documentation.

Page finale de la documentation

La création de la documentation est simplifiée et standardisée facilement. Chaque langage de programmation dispose de sa propre version de Javadoc.

Publié dans Développement, IMT Mines Alès | Laisser un commentaire

Plugin WordPress simple pour Google Calendar

Suite à plusieurs problèmes avec un plugin de Google Calendar et à la suite de la création d’un plugin très simple pour Twitter. Je me suis posé la question de réaliser le même plugin pour accéder à un calendrier publique des conférences et séminaires EuroMov DHM dont l’URL est publique : https://calendar.google.com/calendar/embed?src=at9mn12tnpc5p25u6k8l9ksd44%40group.calendar.google.com&ctz=Europe%2FParis.

La première chose à obtenir est un code appelé API_KEY via la Console Google. Pour cela je vous renvoie vers ces 2 tutoriels qui m’ont bien aidée à fabriquer cette clef.

Cette clef de la forme « a4db08b757294ba98c08f2df493465a1 » est à conserver et à ne pas diffuser, car elle a des droits sur vos calendriers et vos évènements. Dans les exemples, j’utiliserai la clef ci-dessus qui n’est pas valide pour indiquer que vous devez la remplacer par votre propre clef.

Une fois la clef obtenue, il faut disposer de l’ID du calendrier. Donc soit on le retrouve dans l’URL de partage, soit dans le menu « Paramètres et partage » du calendrier sur le site https://calendar.google.com/.

ID de l’agenda « séminaire » menu « Paramètres et partage »

Ou dans l’URL dans l’attribut src le %40 correspondant au symbole @:

https://calendar.google.com/calendar/embed?src=at9mn12tnpc5p25u6k8l9ksd44%40group.calendar.google.com&ctz=Europe%2FParis

Création du plugin dans WordPress

On peut créer simplement un dossier calendar_short_code dans le dossier wp-content/plugins de notre WordPress pour commencer avec un fichier calendar_short_code.php.

Voici le premier jet du programme minimaliste qui permet d’afficher le titre d’un évènement du Google Agenda :

<?php
/**
 * Plugin Name: Calendar Pierre JEAN
 * Plugin URI: https://pierrejean.wp.imt.fr
 * Description: Google calendar
 * Version: 0.1
 * Text Domain: calendar_short_code
 * Author: Pierre JEAN
 * Author URI: https://pierrejean.wp.imt.fr
 */


 function calendar_short_code($arguments){    
    $API_KEY = "a4db08b757294ba98c08f2df493465a1"; 
    $CALENDRIER = "at9mn12tnpc5p25u6k8l9ksd44@group.calendar.google.com";
    $evenements = json_decode(file_get_contents("https://www.googleapis.com/calendar/v3/calendars/{$CALENDRIER}/events?key={$API_KEY}"));
      
    $items = $evenements->items;


    $content = <<<DOC
<div id="calendar_short_code"> 
   <ul>    
DOC;

    foreach( $items  as $key => $item ):
        $content .= <<<DOC
        <li> $item->summary
        </li>            
DOC;
        
    endforeach;


    $content .= <<<DOC
  </ul>
</div> <!-- /calendar_short_code -->
DOC;

    return $content;
}


add_shortcode('calendar_short_code', 'calendar_short_code');
?>

Pour utiliser ce programme, il faut en premier lieu indiquer une clef API_KEY valide obtenue précédemment, activer le plugin dans le backoffice de WordPress, créer une page avec le shortcode [calendar_short_code] et consulter cette page pour vérifier que tout fonctionne.

Affichage du résultat du Google Agenda dans une page WordPress

Pour minimiser le nombre d’appels à Google Calendar à chaque affichage de la page, on peut faire évoluer le code pour remplacer cette ligne :

    $evenements = json_decode(file_get_contents("https://www.googleapis.com/calendar/v3/calendars/{$CALENDRIER}/events?key={$API_KEY}"));

Par un appel aux fonctions get_transient et set_transient qui conservent les informations pendant un lap de temps (300 secondes) :

    $evenements = get_transient( 'calendar_short_code' );
    if( ( false === $evenements ) || ( "" == $evenements ) ) {
    $evenements = json_decode(file_get_contents("https://www.googleapis.com/calendar/v3/calendars/{$CALENDRIER}/events?key={$API_KEY}"));
        set_transient( 'calendar_short_code', $evenements, 300 );
    }

En effet, Google limite le nombre d’utilisations de l’API_KEY pour facturer en cas de sollicitations importantes. Cette solution permet de conserver les informations en local, ce qui est pratique pour un agenda dont les évènements ne changent pas trop souvent.

On peut consulter l’ensemble des informations de l’API Google Calendar sur le site de l’entreprise : https://developers.google.com/calendar/api/guides/overview.

Publié dans CTN, Développement | Laisser un commentaire

Utilisation de Jenkins avec un simple projet Java en local

L’objectif est de tester l’outil d’intégration Jenkins de manière très simple. Du coup, j’ai choisi de ne pas utiliser d’outils de gestion de code sources de type git, svn ou autre, mais juste de lancer un simple test en automatique.

Une autre contrainte a été d’utiliser Jenkins dans un conteneur Docker qui est démarré simplement via cette commande :

docker run --name=jenkins -p 8080:8080 -p 50000:50000 --restart=on-failure jenkins/jenkins:lts-jdk11

Le conteneur Jenkins est accessible via le navigateur sur l’URL http://127.0.0.1:8080/ avec les plugins recommandés.

Maintenant, on va fabriquer le code java basique qui ne fait que tester une fonction qui double un nombre passé en paramètre, voici le code :

public class Calcul {
	
	public static int doublement(int nombre) {
		return nombre * 2;
	}

}

Et le test avec Junit 5

class CalculTest {

	@Test
	void testDoublement() {		
		assertEquals(Calcul.doublement(0) , 0);
		assertEquals(Calcul.doublement(1) , 2);
	}

}

Le test est vraiment basic juste pour vérifier le fonctionnement. Maintenant pour tester en ligne de commande en pur Java (qui est aussi utiliser avec Jenkins d’ailleurs ), on peut utiliser cette outil de Junit en ligne de commande.

Télécharger le fichier junit-platform-console-standalone-1.8.2.jar de Console Launcher : https://junit.org/junit5/docs/snapshot/user-guide/#running-tests-console-launcher qui permet d’appeler l’outil de test Junit5 via une ligne de commande.

En local on peut donc lancer cette commande, par exemple dans le workspace:

java -jar c:\DEV20\eclipse-workspace-jee\JenkinsDemo\junit-platform-console-standalone-1.8.2.jar -cp c:\DEV20\eclipse-workspace-jee\JenkinsDemo\bin --scan-classpath  --disable-banner

Voici la sortie détaillée :

.
+-- JUnit Jupiter [OK]
| '-- CalculTest [OK]
|   '-- testDoublement() [OK]
'-- JUnit Vintage [OK]

Test run finished after 44 ms
[         3 containers found      ]
[         0 containers skipped    ]
[         3 containers started    ]
[         0 containers aborted    ]
[         3 containers successful ]
[         0 containers failed     ]
[         1 tests found           ]
[         0 tests skipped         ]
[         1 tests started         ]
[         0 tests aborted         ]
[         1 tests successful      ]
[         0 tests failed          ]

Ou la version avec erreur d’un test :

.
+-- JUnit Jupiter [OK]
| '-- CalculTest [OK]
|   '-- testDoublement() [X] expected: <2> but was: <1>
'-- JUnit Vintage [OK]

Failures (1):
  JUnit Jupiter:CalculTest:testDoublement()
    MethodSource [className = 'test.CalculTest', methodName = 'testDoublement', methodParameterTypes = '']
    => org.opentest4j.AssertionFailedError: expected: <2> but was: <1>
       org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
       org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62)
       org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:150)
       org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:145)
       org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:527)
       test.CalculTest.testDoublement(CalculTest.java:14)
       java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
       java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
       java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
       java.base/java.lang.reflect.Method.invoke(Method.java:568)
       [...]

Test run finished after 47 ms
[         3 containers found      ]
[         0 containers skipped    ]
[         3 containers started    ]
[         0 containers aborted    ]
[         3 containers successful ]
[         0 containers failed     ]
[         1 tests found           ]
[         0 tests skipped         ]
[         1 tests started         ]
[         0 tests aborted         ]
[         0 tests successful      ]
[         1 tests failed          ]

Pour disposer seulement de cette réponse en cas d’erreur, il faut ajouter le paramètre « –details=none » qui n’affichera qu’en cas d’erreur.

À présent, on peut copier tout le dossier du projet dans le conteneur Jenkins, car il n’a pas accès au dossier c:\DEV20\eclipse-workspace-jee\JenkinsDemo\ mais on aurait pu monter dans le conteneur Jenkins le dossier.

docker cp C:\DEV20\eclipse-workspace-jee\JenkinsDemo jenkins:/home

Donc l’ensemble des fichiers du projet et l’outil junit-platform-console-standalone-1.8.2.jar de Console Launcher se trouve dans le dossier /home/JenkinsDemo. On peut donc ensuite tester dans le conteneur comme ceci.

docker exec --user=root --workdir=/home/JenkinsDemo/ -it  jenkins  /bin/bash

On peut donc ensuite tester notre test dans le conteneur :

java -jar /home/JenkinsDemo/junit-platform-console-standalone-1.8.2.jar -cp /home/JenkinsDemo/bin/  --scan-classpath  --disable-banner --deta
ils=none

Passons maintenant dans Jenkins pour créer un job, « Construire un projet free-style » puis il faut utiliser le bouton « Avancé » pour avoir accès au dossier :

Puis, il faut indiquer le dossier de travail :

Maintenant, on va ajouter notre script comme critère de Build :

La commande testée précédemment doit être entrée pour valider le projet:

java -jar /home/JenkinsDemo/junit-platform-console-standalone-1.8.2.jar -cp /home/JenkinsDemo/bin/  --scan-classpath  --disable-banner --details=none

On peut ensuite sauvegarder les modifications et lancer ce job via « Lancer ce build » et on peut vérifier l’exécution dans le résultat de la console :

Cela permet de vérifier que les opérations s’exécutent correctement. A l’inverse voici une sortie de console en erreur après un échec du test :

Pour conclure et simplifié, on aurait pu monter le dossier du projet Eclipse directement dans le /home du conteneur avec cette commande :

docker run -it -v C:\DEV20\eclipse-workspace-jee\JenkinsDemo\:/home/JenkinsDemo/ --name=jenkins -p 8080:8080 -p 50000:50000 --restart=on-failure jenkins/jenkins:lts-jdk11

Le reste serait identique et grâce à Java portable entre Windows et Linux du conteneur, les commandes avec JUnit sont compatibles.

Publié dans Développement, IMT Mines Alès | Laisser un commentaire

Page de Template WordPress 6 thème FSE

Le nouveau modèle de thème de WordPress 6 appelé FSE pour « Full Site Editing » doit permettre d’améliorer l’édition d’un site web WordPress en permettant de modifier l’ensemble du site avec un éditeur dans l’esprit d’outils comme Divi.

L’ancienne technique d’édition de page dans un thème enfant qui était très classiquement la solution va progressivement disparaitre au profit de cette nouvelle technique parfois proche, parfois nouvelle, de création de thème.

Mon premier besoin est de proposer un modèle de page customiser pour par exemple disposer d’une zone à droite de la page avec un flux Twitter.

Je vais en premier m’appuyer sur le thème twentytwentytwo livré avec WordPress 6 pour dupliquer la page wp-content\themes\twentytwentytwo\templates\single.html en single-twitter.html dans le même dossier

Je n’ai pas encore testé, mais si la nouvelle version devait détruire cette page, je pense que le thème enfant sera encore une fois la solution.

Modifions le code initial de la partie tag <main> en ajoutant un attribut style (les puristes ajouterons un code CSS au fichier style.css mais j’ai voulu simplifier)

<main class="wp-block-group" style="background-color:green !important;width:60% !important;float:left;">

Nous avons une zone de blocs de la page qui vont être limité à 60% de la largeur avec un beau fond vert pour bien identifier la zone.

Après le tag de fermeture </main> nous allons ajouter une zone à droite et une zone en bas pour déplacer le reste du contenu de la page au-dessous de notre 2 colonnes, la verte et la jaune :

<!-- /wp:group --></main>

<!-- Ajout d'une zone droite de la page -->
<div id="droite" style="background-color:yellow !important;width:40% !important;float:right;" >
    Zone droite 
    [twitter_short_code]
</div>
<div id="bas" style="clear: both">
    &nbsp;
</div>
Mise en page sur 2 colonnes d’une nouvelle page template

Il faut ensuite ajouter les indications sur cette nouvelle page dans le fichier wp-content\themes\twentytwentytwo\theme.json dans la zone sous « customTemplates » avant la partie pour la page blanck :

"customTemplates": [
		{
			"name": "single-twitter",
			"title": "Page avec Twitter",
			"postTypes": [
				"page",
				"post"
			]
		},
		{
			"name": "blank",
			"title": "Blank",
			"postTypes": [
				"page",
				"post"
			]
		},

Maintenant, nous pouvons créer une page ou un article avec comme modèle de « Page avec Twitter ». Alors cette étape est optionnelle, la simple création du fichier wp-content\themes\twentytwentytwo\templates\single-twitter.html suffit à WordPress pour proposer dans son menu de modèle cette nouvelle page, mais pour les évolutions futures du thème je pense qu’il vaut mieux indiquer « proprement » que cette page est maintenant dans le modèle twentutwentytwo.

Enfin, nous allons éditer la page wp-content\themes\twentytwentytwo\functions.php pour y ajouter notre bout de programme pour Twitter qui ne va qu’afficher le titre du futur plugin.

// Nouveau shortcode twitter_short_code
function twitter_short_code(){
    return "<p>Le compte Twitter</p>";
}

add_shortcode('twitter_short_code', 'twitter_short_code');

Maintenant, modifions le template pour utiliser le shortcode [twitter_short_code] tout simplement en l’inserant dans le template wp-content\themes\twentytwentytwo\templates\single-twitter.html en bonne place :

<div id="droite" style="background-color:yellow !important;width:40% !important;float:right;" >
    Zone droite 
    [twitter_short_code]
</div>
Shortcode appelé depuis une page template créée spécialement

Et voilà, notre shortcode est utilisé comme élément pour construire la page template.

Nous pouvons aussi utiliser ce shortcode dans un bloc d’une page comme n’importe quel autre shortcode tant que notre thème est actif (à la différence d’un plugin). Il suffit de choisir le modèle de bloc de type « code court » (et non pas code comme j’ai fait par erreur).

L’autre option est de faire un plugin dans le dossier wp-content\plugins, il suffit de créer par exemple un dossier twitter_short_code contenant un fichier twitter_short_code.php avec un code presque identique (le short code est différent, car sinon WordPress indique qu’il est déjà existant donc [twitter_short_code_plugin] pour le plugin).

Voici le code de twitter_short_code.php mais n’oubliez pas d’activer le plugin dans le backoffice de WordPress avant de l’utiliser :

<?php
/**
 * Plugin Name: Twitter Pierre JEAN
 * Plugin URI: https://pierrejean.wp.imt.fr
 * Description: Test twitter simplement
 * Version: 0.1
 * Text Domain: twitter_short_code
 * Author: Pierre JEAN
 * Author URI: https://pierrejean.wp.imt.fr
 */


function twitter_short_code_plugin(){
    return "<p>Le compte Twitter</p>";
}

add_shortcode('twitter_short_code_plugin', 'twitter_short_code_plugin');
?>

Bien sûr, il manque l’ensemble du code pour réellement aller chercher les données de l’API Twitter, mais c’est une autre histoire.

Publié dans CTN, Développement | Laisser un commentaire

Ajout d’un dossier de contenu statique à Tomcat 9

Cas pratique, un programme Tomcat doit produire un fichier de données à télécharger, par exemple un fichier backup.csv.

Produire ce dossier dans le dossier WebContent ou webapp n’est pas possible, car à chaque redémarrage d’une nouvelle version de notre application, le contenu d’un de ces dossiers serait régénéré.

La solution vient d’un dossier de contenu dit statique (static en VO), en modifiant le fichier server.xml de notre serveur Tomcat. On identifie le projet qui doit disposer d’un dossier statique (dans l’exemple, c’est le tag <Context> du projet DevWeb) et on peut y ajouter les instructions pour indiquer à Tomcat qu’un URL peut accéder à ce dossier.

Il faut changer le tag <Context/> en tag double <Context></Context> et y ajouter ces informations de ressources. (Les doubles slashs sont à adapter au type de système d’exploitation).

<Context docBase="DevWeb" path="/DevWeb" reloadable="true" 			source="org.eclipse.jst.jee.server:DevWeb">
  <Resources>
    <PreResources
base="C://Users//pierre.jean//Site-Web//data" 							className="org.apache.catalina.webresources.DirResourceSet"		 webAppMount="/static" />
  </Resources>
</Context>

L’URL pour accéder à la ressource est relative à l’URL du <Context> englobant, notre url est donc http://127.0.0.1:8080/DevWeb/static/backup.csv

Pour lister le contenu du dossier, la première solution est de modifier le fonctionnement général de Tomcat en modifiant le fichier web.xml. Il faut changer la valeur de la servlet par défaut gérant la liste des dossiers en passant à la valeur true la propriété listings comme indiqué ci-dessous.

    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>true</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

La seconde option est de créer dans le dossier WebContent/WEB-INF/ un fichier tomcat-web.xml (ou web.xml mais Tomcat ne le conseille pas pour éviter de déployer ce fichier spécifique dans un serveur autre que Tomcat).

Le contenu du fichier tomcat-web.xml est donc le suivant pour surcharger les réglages par défaut du fichier web.xml généraliste au niveau du serveur Tomcat:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
	<servlet>
		<servlet-name>default</servlet-name>
		<servlet-class>
			org.apache.catalina.servlets.DefaultServlet
		</servlet-class>
		<init-param>
			<param-name>debug</param-name>
			<param-value>0</param-value>
		</init-param>
		<init-param>
			<param-name>listings</param-name>
			<param-value>true</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>default</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>

Après, il n’est pas forcément recommandé en production de donner des accès à un dossier tout entier, mais dans le contexte du projet de recherche, Tomcat est prévu dans un container en local de la machine.

On peut aussi modifier le rendu par défaut de la liste des contenus d’un dossier dans TOmcat mais je vous renvoie sur la documentation, ce n’est pas un besoin que j’ai eu.

Publié dans Développement | Laisser un commentaire

Émission d’un email en Java via un hébergement OVH

Envoyer un email technique depuis un code Java via un compte configuré chez OVH est assez simple si on a trouvé la bonne combinaison de paramètres…

Voici la liste des réglages pour un compte que l’on va appeler publication@euromov.eu et dont le mot de passe a été généré par la console OVH. Attention, l’envoie d’un trop grand nombre d’emails entrainera le blocage de votre compte par OVH. L’entreprise surveille ce genre de fonctionnalité car régulièrement les sites web et autres serveurs de l’entreprise servent à des pirates pour envoyer des emails en masse souvent à l’insu du propriétaire du compte chez OVH.

Le réglage mail.debug est à mettre à true au besoin pour voir les messages d’échange entre le serveur et votre code Java:

Properties prop = new Properties();
prop.put("mail.debug", "false");
prop.put("mail.smtp.auth", "true");		
prop.put("mail.smtp.ssl.protocols", "TLSv1.2");
prop.put("mail.smtp.host", "ssl0.ovh.net");
prop.put("mail.smtp.starttls.enable", "true"); 
prop.put("mail.smtp.ssl.trust", "ssl0.ovh.net");
prop.put("mail.smtp.port", "587");
prop.put("mail.smtp.socketFactory.port", "587");
prop.put("mail.smtp.socketFactory.class","javax.net.ssl.SSLSocketFactory");	Session session = Session.getInstance(prop, new Authenticator() {
  @Override
   protected PasswordAuthentication getPasswordAuthentication() {
	return new PasswordAuthentication("publication@euromov.eu", "MOT_DE_PASSE_A_GENERER_DANS_CONSOLE_OVH");}
   });
		
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress("publication@euromov.eu"));
message.setRecipients(
Message.RecipientType.TO, InternetAddress.parse("directeurs@toto.com"));
message.setSubject("Mail Subject");
String msg = "This is my first email using JavaMailer";

MimeBodyPart mimeBodyPart = new MimeBodyPart();
mimeBodyPart.setContent(msg, "text/html; charset=utf-8");

Multipart multipart = new MimeMultipart();
multipart.addBodyPart(mimeBodyPart);
message.setContent(multipart);
Transport.send(message);

Pour rappel 2 JARS sont nécessaires pour ce code :

Bon email, mais encore une fois attention aux spams.

Publié dans Développement | Laisser un commentaire