Archives par étiquette : Java/JEE

Tutoriel JShell: Utilisation

Ce tutoriel JShell est une démonstration par la pratique de l’utilisation (et l’utilité) de cet outil. Comme c’est souvent le cas dans ce site personnel, c’est une succession d’exercices pratiques à effectuer et se rendre compte du résultat par soit même. Il s’agit ici de l’utilisation courante de l’outil dans ses deux volets: les commandes JShell et l’exéction d’instructions Java.

Cet tutoriel fait partie d’une serie de quatres articles qui permettront de se familiariser, suffisament, avec JShell afin de pouvoir l’utiliser efficacement et augmenter la productivité avec Java:

  1. Tutoriel JShell: Installation et concepts
  2. Cet article: Tutoriel JShell: Utilisation
  3. Tutoriel JShell: Scripts et scripts de démarrage
  4. Tutoriel JShell: Personnalisation et Import de librairies externes

1. Les variables dans JShell

Quand on exécute une instruction Java qui retourne une valeur, JShell crée automatiquement une variable pour y stocker cette valeur. Exécuter dans l’environnement JShell:

10 + 15

L’affichage de retour indique qu’une variable qui s’appelle $1 contient le résultat de cette opération simple:

$1 ==> 25

On peut obtenir plus d’information à ce sujet grâce à la commande JShell:

/vars

Cette commande affiche la liste des variables en indiquant leurs types. On peut également améliorer le retour d’information immédiat. Exécuter:

/set feedback verbose

Si on exécute, après cela, l’opération d’addition modifiée:

10.2 + 15.3

Maintenant le retour immédiat est plus explicite. JShell nous informe tout de suite qu’il a créé une variable $2 de type double et affiche son contenu.

Remarquer que les variables créées implicitement portent un nom qui commence par le caractère $ suivi par un indice qui s’incrémente à chaque nouvelle création.

On remarque également que le type de la variable créée s’adapte au type du résultat de l’instruction exécutée.

Les variables peuvent être également indiquées explicitement avec un nom plus significatif. Dans ce cas le type doit être également indiqué:

double rayon = $2

Vérifier avec la commande /vars que le contenu de la variable rayon est bien égale à celui de la variable $2. Le résultat d’une instruction peut également être affecté
à une variable explicite:

double surface = Math.pow(rayon,2) * Math.PI

Ce qui calcule la surface d’un cercle en élevant son rayon au carré et en multipliant le résultat par π .

2. Vérifier le comportement de Java avec JShell

Une des applications importantes de JShell est de pouvoir vérifier rapidement les résultats retournés par des instructions Java. Exécuter les trois instructions suivantes l’une après l’autre:

Math.round(23.4999999)
Math.round(23.5000001)
Math.round(23.5000000)

Ce test montre que la methode Math.round(double) retourne bien la valeur arrondie à l’entier le plus proche du décimal passé en paramètre (les deux premières exécutions).

Il montre surtout que si la valeur est à mi-chemin entre deux valeurs entières, c’est l’entier supérieur qui est retourné.

Maintenant, on se propose de tester un comportement, souvent mal compris, des instances de classes. Mais d’abord partons d’un environnement propre. Exécuter:

/vars
/list

La session JShell accumule, au fur et à mesure qu’on l’utilise, une liste de variables et d’instructions (snippets) qui ont été créées et exécutées.

On peut remettre l’environnement à zéro, notamment pour commencer de nouveaux tests. Pour cela exécuter:

/reset

Après une éventuelle vérification, exécuter les instructions dans l’ordre:

Date d1 = new Date()
Date d2 = d1
Date d3 = (Date)d1.clone()
/vars

La dernière commande permet d’afficher les 3 variables d1, d2 et d3 avec leur valeur qui est la même. Maintenant, modifions la valeur de d1 pour mettre l’heure à minuit:

d1.setHours(0)
/vars

L’explication du résultat, qui est tout à fait normal, est que la variable qui contient une instance de classe n’est en réalité qu’une référence d’un emplacement mémoire qui contient, lui, les données de cette instance. Il faut comprendre le contenu de ces variables comme une adresse de mémoire et non l’instance elle-même. Ceci dit, les trois instructions font ce qui suit:

  1. Créé une instance de la classe Date et affecte la référence à cette instance (adresse mémoire de l’instance) à la variable d1.
  2. Affecte la valeur de d1 à d2. C’est la référence à l’instance qui est affectée. Donc d1 et d2 pointent sur la même instance.
  3. La méthode clone() créé une nouvelle instance dont les données sont identiques à l’instance clonée. d3 contient donc la référence d’une autre instance de la classe Date.

3. Tester le code d’une méthode

A titre d’exemple, on se propose de développer une méthode qui calcule la clé d’un numéro de sécurité sociale en France. Bien qu’assez triviale, cette méthode nécessite plusieurs instructions qui s’enchaînent. Pour saisir l’ensemble du code, suivre le contenu qui correspond à une ligne de code par l’appui sur la touche [ENTREE], comme dans tout IDE. Commençon par la première ligne:

long cle(String numero) {

Après la touche [ENTREE], JShell répond par:

...>

Et attend la suite des lignes. Continuer la saisie, ligne par ligne:

...> String n = numero.replaceAll("2A", "18");
...> n = n.replaceAll("2B", "19");
...> long ln = Long.parseLong(n);
...> long resultat = 97 - (ln % 97);
...> return resultat;
...> }

A la saisie de la dernière ligne qui constitue, syntaxiquement, la fin de la méthode, JShell termine la saisie et affiche un message qui nous informe (sous reserve qu’il n’y a pas d’erreur de saisie) que la méthode a été créée. On peut le vérifier par la commande:

/methods

On peut également l’exécuter:

cle("2690299341732")

Bien entendu vous pouvez utiliser votre propre numéro de sécurité sociale (les 13 chiffres) pour calculer et vérifier la clé.

Ceux qui sont nées en Corse remarqueront qu’il y a un bug dans ce code qu’il faudra, donc, corriger. Pour le faire on doit éditer la méthode. C’est la commande edit qui permet cela. D’abord parametrons un éditeur de texte disponible sur le système utilisé. Pour ma part j’utilise vi:

/set edit vi

Puis

/edit cle

L’éditeur paramétré s’ouvre avec le texte du code de la méthode et on peut alors le modifier. Remplacer le nombre 18 dans la 2eme ligne par 19 et le nombre 19 dans la 3eme ligne par 18. Ensuite enregistrer la modification et quitter.

Tutoriel JShell: Installation et concepts

JShell est l’utilitaire Shell (ou REPL, pour Read Eval Print Loop) du langage de programmation Java. Un tel outil manquait cruellement à Java jusqu’à sa récente version 9 où il a été introduit. JShell permet d’augmenter considérablement la productivité du développement avec ce langage. Il permet, notament, d’ffectuer de manière très simple, rapide et intéractive des tests d’exécution d’instructions Java (snippets).

Cet article a pour objectif de présenter la manière d’installer ainsi que les concepts d’utilisation de cet outil. Il fait partie d’une serie de quatre articles qui permettront de se familiariser, suffisament, avec JShell afin de pouvoir l’utiliser efficacement et augmenter la productivité avec Java:

  1. Cet article: Tutoriel JShell: Installation et concepts
  2. Tutoriel JShell: Utilisation
  3. Tutoriel JShell: Scripts et scripts de démarrage
  4. Tutoriel JShell: Personnalisation et Import de librairies externes

1. Installer JShell

JShell fait partie du JDK. Il a été introduit à la version 9 de Java et de ce fait il faudra installer au minimum la version 9 du JDK pour pouvoir y avoir accéder. Sur Ubuntu installer la variante headless d’OpnenJDK 9 ou plus.

sudo apt install openjdk-11-jdk-headless

Après cela JShell sera disponible. La vérification peut se faire par la commande:

jshell -version

Cette commande répond par l’affichage de la version de JShell. La version majeure retournée (premier nombre) doit être la même que celle du JDK installé.

2. Les bases de fonctionnement de JShell

JShell est utilisé, principalement, de manière interactive. Son interface est une console interactive en ligne de commande. Pour y accéder utiliser la commande:

jshell

Suite à cela on obtient le prompt suivant qui attend la saisie de commandes à la syntaxe JShell ou Java.

|  Welcome to JShell -- Version 10.0.1
|  For an introduction type: /help intro

jshell> _

A chaque commande introduite (saisie de la commande + [Entrée] du clavier), JShell répond immédiatement par l’affichage du résultat de la commande ou par un message d’erreur si la syntaxe est fausse ou qu’une erreur d’exécution s’est produite.

  • Les commandes propres à JShell commencent toujours par le caractère slash: /
  • Toute commande introduite qui ne commence pas par ce caractère doit être conforme à la syntaxe Java.

3. Les commandes JShell

Ces commandes qui commencent obligatoirement par le caractère slash servent à contrôler le fonctionnement de l’outil. Une commande particulière permet d’avoir une aide à propos de ces commandes:

/help

Affiche une liste exhaustive des commandes disponibles (relative à la version utilisée, bien entendu). On peut également afficher l’aide d’une commande en particulier quand on connait sa syntaxe:

/help exit

Affiche une aide sur la commande JShell exit. Grâce à cette commande, on sait maintenant qu’il faudra utiliser la commande /exit pour quitter la console JShell.

De la même manière, déterminer la fonction de la commande history. Ensuite utiliser cette commande pour voir le résultat:

/history

Cette commande permet d’afficher l’hitorique de toutes les commandes exécutées depuis le début de la session JShell courante. Si tout va bien le résultat devrait être donc:

/help
/help exit
/history

4. Exécuter une instruction Java

Dans la console JShell exécuter la commande:

exit

La réponse est un message d’erreur. Pourtant, on l’a vu, exit est bel et bien une commande correcte de JShell. L’explication est dans l’absence du caractère slash au début. Cette absense fait en sorte que JShell considère qu’il s’agit d’une instruction Java. exit n’est pas une instruction Java valide, d’où l’erreur.

Exécuter maintenant l’instruction (faire attention à la syntaxe, le mieux est de faire un copier/coller):

"Java JShell".substring(5,11)

La réponse doit être:

$1 ==> "JShell"

Cette réponse signifie que maintenant une variable appelée $1 contient la valeur chaine de caractère ‘JShell‘.

Explication: A l’exécution de l’instruction Java qui retourne un résultat, JShell crée une variable qui s’appelle $x, x étant un nombre entier, et lui affecte le résultat de l’instruction. Ensuite il affiche le contenu de cette variable. Pour s’en rendre compte, exécuter maintenant:

System.out.println($1)

La méthode substring(iDebut, iFin) de la classe String, retourne une sous chaine de caractère qui commence à l’indice de cracatère iDebut et fini à l’indice iFin. D’où le résultat obtenu.

Exécuter maintenant l’instruction modifiée:

"Java JShell".substring(5,12)

L’indice de caractère (de fin) maximum dans cette chaine étant 11 (car elle contient 11 caractères), cette instruction lève l’exception java.lang.StringIndexOutOfBoundsException exactement comme à son exécution dans un programme Java.

Installer des packages personnels sur un système Ubuntu et compatibles

Objectif: installer des packages non officiels sur un système GNU/Linux Ubuntu ou compatibles, notamment Debian pour les machines serveurs. Le caractère non officiel des packages peut être la conséquence d’existence de licence particulière qui les rend non libres au sens strict. Le JRE/JDK d’Oracle se trouve dans ce cas.

Avertissement: Certains packages disponibles dans des dépôts non officiels peuvent être dans un état instable ou existent pour des raisons expérimentales. Il convient de vérifier leur statut avant de les installer.

1. Installation standard

L’installation de ces packages se fait de manière standard avec l’utilitaire en ligne de commande apt-get.

sudo apt-get install oracle-java8-installer

Cette commande d’instillation du JDK Oracle version 8 ne fonctionne pas par défaut. Pour qu’elle fonctionne il faudra d’abord configurer la liste des dépôts de packages utilisables.

2. Configurer la liste des dépôts de packages

Les packages du JDK d’Oracle se trouvent sur le site Launchpad: https://launchpad.net. Ils sont maintenus sur ce site par l’équipe Web Upd8http://www.webupd8.org. Il s’agit d’un site qui héberge des PPA (Personal Packages Archives).

Pour ajouter cet emplacement comme source des packages installables par apt-get, il faudra utiliser la commande:

sudo add-apt-repository ppa:webupd8team/java

Cette commande veut dire ajouter à la liste des sources utilisables par apt-get le dépôt PPA de l’utilisateur webupd8team, catégorie java.

Toutefois cette commande ne fonctionne pas par défaut (on peut avoir l’erreur « add-apt-repository: command not found »). Si c’est le cas, il faudra d’abord installer l’utilitaire add-apt-repository qui se trouve sur les dépôts officiels du Système Ubuntu ou compatibles. Cette installation se fait avec la commande:

sudo apt-get install software-properties-common

Après l’ajout du dépôt ppa:webupd8team/java, il convient de mettre à jour le cache de l’utilitaire apt-get:

sudo apt-get update

Après cela la commande d’installation des packages non standard invoquée en (1), fonctionnera.

Tutoriel Maven – Tests unitaires Java

Objectif: Ce tutoriel Maven concerne la mise en place de test unitaire java avec cet outil. Par défaut, à la génération d’un nouveau projet, Maven met en place tout ce qu’il faut pour intégrer un test unitaire java. Il s’agit de la branche test de l’arborescence créée, cf. l’article Tutoriel Maven 3 – partie 2. Dans ce tutoriel Maven on verra comment mettre en place ce test unitaire java, comment le contourner en cas de besoin et l’intérêt de cette fonctionnalité dans le processus de développement de logiciels.
Pré-requis: le JDK est installé, cf. l’article: Installer Java sur Ubuntu ou compatible et Maven 3 est installé cf. l’artice: Tutoriel Maven 3 – partie 1

1. Maven test unitaire Java est mis en place par défaut à la génération du projet

mvn archetype:generate -DgroupId=com.example.calculateur \
    -DartifactId=calculteur -Dversion=1.0 -Dpackage=com.example.calculateur \
    -DinteractiveMode=false
ls calculateur/src/test/java/com/exemple/calculateur/
AppTest.java

La classe AppTest.java a été générée pour contenir le code des tests.
La dépendance au framework JUnit a été rajoutée dans le fichier pom.xml

.....
<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>3.8.1</version>
    <scope>test</scope>
  </dependency>
</dependencies>
.....

2. Simuler une erreur de test unitaire Java

Dans le code de la classe AppTest.java, repérer:

public void testApp()
{
   assertTrue( true );
}

Et modifier en:

public void testApp()
{
   assertTrue( false );
}

Après cela si on lance la reconstruction de l’application, l’opération échoue à cause de test non concluant.

mvn clean package
..
..
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.exemple.calculateur.AppTest
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.46 sec <<< FAILURE!

Results :

Failed tests:   testApp(com.exemple.calculateur.AppTest)

Tests run: 1, Failures: 1, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE

3. Ajouter les fonctions de calcul à l’application

Ajouter une classe qui se charge des calculs: Calc.java et activer l’appel au calcul dans App.java

package com.exemple.calculateur;
public class Calc
{
  private int getInt(String s) {
    int resultat = -1;
    try {
      resultat = Integer.parseInt(s);
    }
    catch (NumberFormatException e) {
      resultat = -1;
    }
    finally {
      return resultat;
    }
  }
  public int somme(String s1, String s2)
  {
    int i1 = getInt(s1);
    int i2 = getInt(s2);
    int resultat = i1 + i2;
    return resultat;
  }
  public int multiplication(String m1, String m2)
  {
    int i1 = getInt(m1);
    int i2 = getInt(m2);
    int resultat = i1 * i2;
    return resultat;
  }
}
// Dans App.java
public static void main( String[] args )
{
  if (args.length != 3) {     
    System.out.println("Opération non supportée.");     
    System.exit(1);   
  }
  String operation = args[1];
  Calc calc = new Calc();
  int resultat = -1;
  if (operation.equalsIgnoreCase("+")) {
    resultat = calc.somme(args[0], args[2]);
  } else if (operation.equalsIgnoreCase("x")) {
    resultat = calc.multiplication(args[0], args[2]);
  } 
  if (resulat >= 0) {
    System.out.println("Résultat: " + resultat);
  } else System.out.println("Opération non supportée");
}

Ce code a été écrit pour répondre au cahier des charges suivant:

  1. Le calculateur peut effectuer les opérations d’addition et de multiplication.
  2. Les opérations ne portent que sur des nombres entiers positifs ou nul.
  3. On passe trois arguments au calculateur, dans l’ordre: 1er opérande, opération, 2eme opérande.
  4. En cas d’erreur d’utilisation le calculateur répond par le message « Opération non supportée ».

4. Ajouter le test unitaire Java en conformité avec le cahier des charges

Ce test doit contrôler le fonctionnement des méthodes de la classe Calc qui effectue le calcul et constitue le cœur de cette application. Commençons par la méthode Calc.somme(String s1, String s2). Dans AppTest.java on va modifier le code, cette fois pour inclure les tests.

public void testApp()
{
  Calc calc = new Calc();
  assertEquals(calc.somme("321", "425"), 746);
  assertTrue(calc.somme("abc", "cba") < 0);
  assertTrue(calc.somme("abc", "653") < 0);
  assertTrue(calc.somme("-145", "153") < 0);
}

A la construction de l’application le troisième test échoue. En révisant le code de la classe Calc, on s’aperçoit de l’erreur et on corrige le code de la méthode Calc.somme(String s1, String s2)

public int somme(String s1, String s2)
{
  int resultat = -1;
  int i1 = getInt(s1);
  int i2 = getInt(s2);
  if ((i1 >= 0) && (i2 >= 0)) resultat = i1 + i2;
  return resultat;
}

Après cette correction, la construction de l’application se fait sans échec. Vous pouvez vous exercer en mettant en place les tests pour la deuxième méthode de multiplication.

5. Désactiver le test unitaire Java

Dans certains cas, on pourrais avoir besoin de désactiver la phase de test afin de forcer la construction de l’application. Cela peut se faire de deux façons.

5.1. En ligne de commande

mvn -Dmaven.test.skip=true package

5.2. En modifiant le fichier pom.xml

Dans le fichier pom.xml du projet, ajouter la référence au plug-in surefire. Remarquer le paramètre: <skipTests>true</skipTests>

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.18.1</version>
  <configuration>
    <skipTests>true</skipTests>
  </configuration>
</plugin>