R Studio : https://posit.co/download/rstudio-desktop/
NOTE Mysql installation avec Windows:
Windows 64-bit, ZIP Archive puis ne pas « créer de compte » juste « No thanks, just start my download. »
Installation si nécessaire de « Redistribuable Visual C++ pour Visual Studio 2012 » fichiers vcredist_x64.exe et vcredist_x86.exe depuis https://www.microsoft.com/fr-fr/download/details.aspx?id=30679
En cas de soucis, possibilité d’installer MAMP : https://www.mamp.info/en/downloads/
Accès aux fichiers pour TP
Liens vers le fichier SQL ci-dessous pour importation dans une base de données :
L’accès à un serveur de base de données Mysql suppose plusieurs informations techniques, l’adresse du serveur de base de données (ici 127.0.0.1), un compte sur cette base de données (ici root), un mot de passe correspondant à ce compte (ici « » pour indiquer mot de passe vide), un port de base de données (ici et par défaut 3306 mais sur Mamp cela peut être 8889) et enfin une base de données (ici sante)
DB <- dbConnect(MySQL(), user="root", host="127.0.0.1", password="",dbname="sante" , port=3306)
Une fois la connexion réalisée, on peut lister les tables dans cette base de données sante.
dbListTables(DB)
Et enfin poser des questions en SQL
resultat <- dbGetQuery(DB, "SELECT * FROM data")
print( resultat )
Importation en CSV
La première étape est de trouver le dossier dans lequel Mysql accepte l’importation d’un fichier CSV, la requête SQL est la suivante :
SHOW VARIABLES LIKE "secure_file_priv"
Dans mon cas le dossier est dans c:\Developpement\Wamp64\tmp\ du coup je peux placer mon fichier analyse.csv dans ce dossier et lancé cette requête SQL après avoir créé la table analyse :
LOAD DATA INFILE 'c:/Developpement/wamp64/tmp/analyse.csv'
INTO TABLE analyse
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\r\n'
IGNORE 1 ROWS;
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 …
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.
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.
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.
La création de la documentation est simplifiée et standardisée facilement. Chaque langage de programmation dispose de sa propre version de Javadoc.
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;
}
}
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:
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.
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.
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:
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 :
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).
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.
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:
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.
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 :
Installation de WAMP : https://www.wampserver.com/ ou MAMP : https://www.mamp.info/en/downloads/
Supposons un accès à un fichier « sante.accdb » contenant une table data de cette nature :
id date_analyse glycemie
1 2022-03-17 10
2 2022-03-18 20
On peut interroger en langage R en SQL vers Microsoft Accces.
En premier lieu il faut installer le module pour R pour accéder à Accces:
install.packages("RODBC")
library(RODBC)
Une fois l’installation réalisée de ce module, on peut ouvrir le fichier Accès et intérroger en SQL la base de données:
con <- odbcConnectExcel2007("C:/Users/utilisateur/Documents/sante.accdb")
resultat <- sqlQuery(con,"select date_analyse from data")
print( resultat )
On peut faire de manière similaire sur MySQL avec des commandes similaires, en premier lieu en installant le module pour la liaison avec le serveur de base de données MySQL :
install.packages("RMySQL")
library(RMySQL)
L’accès à un serveur de base de données Mysql suppose plusieurs informations techniques, l’adresse du serveur de base de données (ici 127.0.0.1), un compte sur cette base de données (ici root), un mot de passe correspondant à ce compte (ici « » pour indiquer mot de passe vide), un port de base de données (ici et par défaut 3306) et enfin une base de données (ici sante)
DB <- dbConnect(MySQL(), user="root", host="127.0.0.1", password="",dbname="sante" , port=3306)
Une fois la connexion réalisée, on peut lister les tables dans cette base de données sante.
dbListTables(DB)
Et enfin poser des questions en SQL
resultat <- dbGetQuery(DB, "SELECT * FROM data")
print( resultat )
Test unitaire en R
Installation de l’outil de test
install.packages("testthat")
library(testthat)
Test d’une valeur égale via :
expect_equal( 95 , dbGetQuery(DB, "SELECT max(age) from import")[,1] )
Pas de réponse si tout va bien, mais si on test ceci :
expect_equal( 92 , dbGetQuery(DB, "SELECT max(age) from import")[,1] )
Error: 92 not equal to dbGetQuery(DB, "SELECT max(age) from import")[, 1].
1/1 mismatches
[1] 92 - 95 == -3
NOTA : la version utilisée dans cette première version est la iText5 une prochaine version utilisera la nouvelle version iText7
En premier lieu il faut récupérer les bibliothèques jar suivantes:
itextpdf-5.5.13.3.jar
log4j-1.2.16.jar
slf4j-api-1.7.9.jar
slf4j-log4j12-1.7.13.jar
Placer ces fichiers dans le dossier WebContent/WEB-INF/lib de votre application Dynamic Web Application , les sélectionner et faire click droit > Build Path > Add to Build Path
Vous pouvez maintenant coder une simple JSP pour générer un ficheir PDF. Attention, si vous cette erreur « getOutputStream() has already been called for this response » il faut retirer tout espace ou retour chariot qui pourrait être interprétés par la JSP comme du HTML à envoyer donc tout se trouve sur une seule ligne pour les tag JSP de type <%@page import %>:
L’outil pktmon.exe disponible par défaut dans Windows 10 Pro permet de faire un peu de dump de paquet TCP.
Bien sûr, la solution de npcap/winpcap/wireshark est beaucoup plus performant, mais avec quelques commandes on peut extraire l’information utile. Je me concentre surtout sur la partie HTTP qui me permet de vérifier les échanges de données même si la limitation de pktmon à ne pas pouvoir fonctionner sur la boucle locale 127.0.0.1 est clairement une limitation pour moi.
Démarrons par ajouter un filtre sur le protocole TCP sur le port 80
On ajoute un nom Port80 et on va récupérer que les paquets marqués TCP de type PUSH (codé PSH) sur le port 80 en provenance de la Mac Address de ma carte réseau 00-AA-BB-CC-DD-EE à remplacer par votre valeur.
Il suffit maintenant d’arrêter la capture est de convertir le fichier PktMon.etl en format lisible à moins d’utiliser l’outil d’analyse de Microsoft Network Monitor.
Le fichier ftp.txt est assez clair pour être lu et vérifier les packets TCP
C:\Windows\system32\pktmon.exe stop
C:\Windows\system32\pktmon.exe format PktMon.etl -o file.txt
Exemple d’accès sur un site web en méthode GET en regardant le contenu du fichier file.txt :
Ensuite on pourra utiliser les outils de développements des navigateurs pour une lecture plus simple, mais la liaison TPC/IP sur le contenu est clairement lisible avec cet outil fourni par défaut.
L’option suivante est soit de faire plus de réseau avec tcpdump ou wireshark qui sont dédiés aux couches réseaux soit de partir en développement web avec les outils embarqués dans les navigateurs Web.
Je pense que cette option semble prometteuse même si je n’arrive pas encore à la faire fonctionner : https://web.archive.org/web/20150726062624/http://ig2600.blogspot.com/2011/03/powershell-script-to-enable-windows-to.html
La nouvelle version de taiga.io avec docker inclus un installateur bien simplifié.
Il me parait utile d’y ajouter simplement l’authentification OTP qui permet d’accepter le mot de passe ajouté avec 6 chiffres calculé par l’horloge à partir d’un code. Vous trouverez des explications sur ce processus dans cet article: https://en.wikipedia.org/wiki/One-time_password
Le seul fichier à éditer pour inclure l’OTP dans taiga est le fichier /taiga-back/taiga/users/services.py dans le containeur taiga-docker_taiga-back_1.
En premier lieu, il faut ajouter au containeur taiga-docker_taiga-back_1 le module pyotp:
Nous pouvons donc éditer le fichier services.py pour y adjoindre et modifier le programme, en premier lieu en important le module pyotp avant toutes les fonctions:
import pyotp
Puis on va chercher la fonction get_and_validate_user qui a cette forme:
def get_and_validate_user(*, username: str, password: str) -> bool:
"""
Check if user with username/email exists and specified
password matchs well with existing user password.
if user is valid, user is returned else, corresponding
exception is raised.
"""
user = get_user_by_username_or_email(username)
if not user.check_password(password) or not user.is_active or user.is_system:
raise exc.WrongArguments(_("Username or password does not matches user."))
return user
Nous allons ajouter l’extraction des 6 derniers symboles du mot de passe pour trouver le code PIN à valider par OTP avec le code fourni en paramètre. Donc la nouvelle version de la fonction get_and_validate_user
def get_and_validate_user(*, username: str, password: str) -> bool:
"""
Check if user with username/email exists and specified
password matchs well with existing user password.
if user is valid, user is returned else, corresponding
exception is raised.
"""
user = get_user_by_username_or_email(username)
"""
PJE verification OTP
"""
totp = pyotp.TOTP("base32secret3232")
passwordPIN = password
password = passwordPIN[:-6]
pin = totp.verify( passwordPIN[-6:] , valid_window=2 )
if pin is False:
raise exc.WrongArguments(_("Username or password does not matches user PIN."))
"""
/ PJE verification OTP
"""
if not user.check_password(password) or not user.is_active or user.is_system:
raise exc.WrongArguments(_("Username or password does not matches user."))
return user
En incluant dans la fonction d’authentification, l’extraction des 6 derniers symboles fournis par l’utilisateur, on peut ajouter une authentification avec code OTP. On note une nouvelle exception envoyé par le serveur en cas d’erreur sur le code PIN.
NOTA: je pensais avoir un problème de Timezone dans le containeur mais au final, je pense que c’est valide comme installation. J’ai testé quelques solutions de changement dans le containeur de timezone mais finalement je pense que je peux aussi m’appuyer sur l’argument for_time de la fonction verify : https://pyauth.github.io/pyotp/_modules/pyotp/totp.html#TOTP.verify