notes·de·pit - Mot-clé - esiParfois j'apprends à pêcher à des gens qui n'aiment pas le poisson2023-06-13T11:41:09+02:00PiTurn:md5:45526db4e4cfb511098640352c276065DotclearJDK13 - JDK14 - JDK15 - JDK16 - JDK17, quelques nouveautésurn:md5:e8574cacb0f2a65ff35ddb1a889a0b2e2021-09-24T10:37:00+02:002021-09-28T23:51:47+02:00PiTCartable au dosenseignementesigeekjava<p><img src="https://blog.namok.be/public/images/divers/2021/champignons.jpg" alt="champignons.jpg, sept. 2021" style="margin: 0 auto; display: block;" /></p>
<p>TL;DR : c'est long !</p>
<p>Je savais qu'avec le changement — l'accélération plutôt — des cycles de sortie
des versions de Java, je ne parviendrais plus trop à suivre. Je m'étais promis de suivre les versions LTS… c'est ce que j'essaie de faire.</p>
<p>Ce billet est la suite des billets
« <a href="https://blog.namok.be/?post/2011/07/11/jdk7">Java 7 is out, quel est son lot de nouveautés ?</a> »,
« <a href="https://blog.namok.be/?post/2014/06/03/jdk8">JDK8, les nouveautés</a> »,
« <a href="https://blog.namok.be/?post/2018/03/22/JDK9">JDK9, les nouveautés</a> » et
« <a href="https://blog.namok.be/?post/2019/04/06/JDK10-11-12">JDK10, 11 et 12, quelques nouveautés</a> »</p>
<p>Ce billet, comme les précédents, ne se veut pas exhaustif, je prends ce que je
trouve intéressant (et comprends) dans les articles d'<em>openjdk</em> sur le sujet ;
<a href="https://openjdk.java.net/projects/jdk/13/">JDK13</a>,
<a href="https://openjdk.java.net/projects/jdk/14/">JDK14</a>,
<a href="https://openjdk.java.net/projects/jdk/15/">JDK15</a>,
<a href="https://openjdk.java.net/projects/jdk/16/">JDK16</a> et
<a href="https://openjdk.java.net/projects/jdk/17/">JDK17</a>. J'aborde parfois les <em>preview</em> et les <em>second preview</em> mais en général, je n'en parle que lorsqu'elles sont inclues dans le JDK.</p>
<h1>JDK 13</h1>
<p>Le <em>switch expression</em> (présenté dans « <a href="https://blog.namok.be/?post/2019/04/06/JDK10-11-12">JDK10, 11 et 12, quelques nouveautés</a> ») est toujours en <em>preview</em> mais ça arrive avec JDK14. .</p>
<p>En <em>preview</em> également, les <strong>blocs de texte</strong> (<em>text blocks</em>, <a href="https://openjdk.java.net/jeps/355">JEP355</a>). Ils arrivent vraiment avec JDK15.</p>
<p>L'implémentation de l'API « socket » est réimplémentée (<a href="https://openjdk.java.net/jeps/353">JEP353</a>). Il s'agit de la classe utilisée par <code>java.net.Socket</code> et <code>java.net.ServerSocket</code>. L'ancienne implémentation contient du vieux code C et Java. Par défaut, ce n'est plus la classe <code>PlainSocketImpl</code> qui implémente <code>java.net.SocketImpl</code> mais <code>NioSocketImpl</code>. L'ancienne implémentation reste disponible dans le JDK et peut être activée par une propriété du système (<em>system property</em>).</p>
<h1>JDK 14</h1>
<h2><em>Switch expression</em></h2>
<p>Arrivée officielle du <strong><em>switch expression</em></strong> défini dans <em>JDK Enhancement Proposal</em> (<strong>JEP</strong>) <a href="https://openjdk.java.net/jeps/361">JEP361</a>. C'est toujours présenté dans « <a href="https://blog.namok.be/?post/2019/04/06/JDK10-11-12">JDK10, 11 et 12, quelques nouveautés</a> ».</p>
<p>Pour rappel, il est maintenant possible d'écrire : </p>
<pre><code class="language-java">
Season s = // a season
String message = switch(s){
case SPRING, SUMMER -> "It's hot";
case WINTER, AUTUMN -> "It's cold";
};
</code></pre>
<h2><em>Helpful NullPointerExceptions</em></h2>
<p>Amélioration du message associé à une <code>NullPointerException</code> (<a href="https://openjdk.java.net/jeps/358">JEP358</a>). Là où le message était lacunaire, il précise maintenant quelle variable est nulle. Exemples extraits de <a href="https://openjdk.java.net/jeps/358">JEP358</a>, là où l'on avait :</p>
<pre><code class="language-java">
a.i = 99;
Exception in thread "main" java.lang.NullPointerException
at Prog.main(Prog.java:5)
</code></pre>
<p>… maintenant, le message pourrait être (en fonction de la situation bien sûr)</p>
<pre><code class="language-java">
Exception in thread "main" java.lang.NullPointerException:
Cannot assign field "i" because "a" is null
at Prog.main(Prog.java:5)
</code></pre>
<p>À ceci s'ajoutent :</p>
<ul>
<li>des modifications au sujet des <em>garbage collector</em> ;</li>
<li>des suppressions de classes annoncées dans les précédentes _release__; </li>
<li>la seconde <em>preview</em> de <em>text blocks</em> qui arrive dans JDK15 ;</li>
<li>la première <em>preview</em> du <em>pattern matching</em> pour l'opérateur <code>instanceof</code> et de la notion de <em>records</em>. On en reparle plus bas avec JDK16. </li>
</ul>
<h1>JDK 15</h1>
<h2><em>Text blocks</em></h2>
<p>Commençons par la <a href="https://openjdk.java.net/jeps/378">JEP378</a> qui concerne <em>text blocks</em>. Les blocs de textes sont simplement des littéraux de type <code>String</code> qui s'étendent sur plusieurs lignes sans devoir s'embarrasser d'ouvrir et fermer des guillemets ou d'ajouter des séquences d'échappement. Les blocs de texte sont formatés comme ils sont écrits.</p>
<p>Un <em>text block</em> commence par 3 guillemets (<code>"""</code>) suivi de 0 ou plusieurs espaces blancs (<em>white space</em>) et d'1 passage à la ligne (<em>line terminator</em>). Il se termine par 3 autres guillemets suivi de 0 ou plusieurs espaces blancs (<em>white space</em>).</p>
<p>Il représente un <strong>littéral de type String</strong> (<em>string literal</em>) à part entière. Il est considéré comme une constante de type <code>String</code> et se retrouve dans la <em>pool</em> des constantes de la classe comme les autres littéraux <code>String</code>.</p>
<p>Voici deux exemples assez parlant (± issu de <a href="https://openjdk.java.net/jeps/378">JEP378</a>) montrant le gain pour les séquences d'échappement et les passage de ligne et à la ligne : </p>
<pre><code class="language-java">
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";
</code></pre>
<pre><code class="language-java">
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
</code></pre>
<pre><code class="language-java">
var query = "SELECT \"EMP_ID\", \"LAST_NAME\" FROM" +
" \"EMPLOYEE_TB\"\n" +
"WHERE \"CITY\" = 'INDIANAPOLIS'\n" +
"ORDER BY \"EMP_ID\", \"LAST_NAME\";\n";
</code></pre>
<pre><code class="language-java">
var query = """
SELECT "EMP_ID", "LAST_NAME" FROM "EMPLOYEE_TB"
WHERE "CITY" = 'INDIANAPOLIS'
ORDER BY "EMP_ID", "LAST_NAME";
""";
</code></pre>
<h2><em>Hidden class</em></h2>
<p>JDK15 introduit (<a href="https://openjdk.java.net/jeps/371">JEP371</a>) les <strong>classes cachées</strong> (<em>hidden class</em>) qui sont des classes qui ne peuvent être utilisées directement par (le <em>bytecode</em>) d'autres classes. Le but est qu'elles soient utilisées par des frameworks qui génèrent des classes au <em>runtime</em> et les utilisent <em>via</em> la réflexion. Du point de vue du développeur ou de la développeuse lambda, cette fonctionnalité a peu d'intérêt.</p>
<p>Lorsqu'une classe, <code>MyClass</code>, est compilée, un fichier <code>MyClass.class</code> lui est associé et contient le <em>bytecode</em> de la classe. Lorsqu'il s'agit d'une classe interne anonyme (<em>inner anonymous class</em>), un fichier <code>MyClass$i.class</code> est créé et contient le <em>bytecode</em> de cette classe interne anonyme. Avec l'avènement des <em>lambdas</em> qui sont une manière d'instancier une classe interne anonyme qui implémente une interface fonctionnelle, le <em>bytecode</em> est généré au <em>runtime</em> sans lui dédier un fichier <code>.class</code>.</p>
<p>Un <em>bytecode</em> généré à l'exécution (<em>runtime</em>) ou à la compilation (<em>compile time</em>) n'est pas différencié par l'API et le <em>bytecode</em> d'une classe pourrait être réutilisé pendant le cycle de vie de l'application alors que ce n'est pas désiré par le ou la développeuse. Les classes cachées (<em>hidden class</em>) semblent adresser ce problème… et ça sort du cadre de cet article… et de ma compréhension actuelle ;-)</p>
<h2><em>Reimplement the Legacy DatagramSocket API</em></h2>
<p>Réécriture de l'API pour les classes <code>java.net.DatagramSocket</code> et<br />
<code>java.net.MulticastSocket</code> (<a href="https://openjdk.java.net/jeps/373">JEP373</a>)</p>
<p>À ceci s'ajoutent :</p>
<ul>
<li>l'ajout d'un algorithme (<em>Edwards-Curve Digital Signature Algorithm (EdDSA)</em>) cryptographique pour le chiffrement elliptique ;</li>
<li>la première <em>preview</em> des classes scellées (<em>sealed class</em>) qui arrivent dans JDK17 (voir ci-dessous) ;</li>
<li>les <em>garbages collector</em> ZGC et Shenandoah ne sont plus expérimentaux. G1, reste le « ramasse miettes » par défaut ;</li>
<li>une partie du code RMI (<em>remote method invocation</em>) est déprécié : RMI Activation ; </li>
<li>… </li>
</ul>
<h1>JDK 16</h1>
<h2><em>Pattern matching for instanceof</em></h2>
<p>La « correspondance de schema » nous dirons <em>pattern matching</em> pour l'opérateur <code>instanceof</code> vise à optimiser (raccourcir) l'usage de cette structure. Structure souvent utilisée en Java lorsque l'on reçoit un objet et que l'on veut tester son type avant de l'utiliser… pour accéder à ses attributs.</p>
<pre><code class="language-java">
if (o instanceof String) {
var s = (String) o;
// do something with string s
}
</code></pre>
<p>Cette structure pourra être raccourcie, pour directement déclarer et instancier une variable dès lors que la condition est vraie, en (noter l'apparition d'un petit <code>s</code> dans les parenthèses) :</p>
<pre><code class="language-java">
if (o instanceof String s) {
// do something with string s
}
</code></pre>
<p>Quelques remarques et définitions car cette notion de <em>pattern matching</em> risque d'être appliquée à d'autres structures.</p>
<p>Un <em>pattern</em> (modèle ?) est un prédicat (un test) qui peut être effectué sur une valeur (une cible, <em>target</em>). Les <em>patterns</em> apparaissent comme les opérandes d'instructions et d'expressions qui fournissent les valeurs à tester. Les <em>patterns</em> déclarent des variables locales appelées <em>pattern variables</em>.</p>
<p>Tester une valeur par rapport à un <em>pattern</em> s'appelle le <em>pattern matching</em> (la comparaison de modèle ?). Le <em>pattern matching</em> est différent de l'exécution d'une instruction et de l'évaluation d'une expression.</p>
<p>Si le prédicat est vrai, le <em>pattern matching</em> initialise la variable locale, la <em>pattern variable</em>. Cette variable locale n'existe que si le prédicat est vrai.
(voir <a href="https://docs.oracle.com/javase/specs/jls/se17/jls17.pdf">JLS17</a> section 14.30)</p>
<p>Le scope d'une <em>pattern variable</em> est tel qu'elle ne peut exister que si le prédicat est vrai. Les deux cas de figures les plus fréquents (et simples) sont les suivants :</p>
<ul>
<li><p>une <em>pattern variable</em> est introduite lorsqu'une expression est <strong>vraie</strong> :</p>
<pre><code class="language-java">
if (o instanceof String s) {
// s in scope and you can do something with string s
} else {
// s not in scope
}
// s not in scope
</code></pre></li>
<li><p>une <em>pattern variable</em> est introduite lorsqu'une expression est <strong>fausse</strong> :</p>
<pre><code class="language-java">
void test(Object o) {
if (!(o instanceof String s)) {
throw new IllegalArgumentException();
}
// this point is only reachable if the pattern match
// succeeded thus, s is in scope for the rest
// of the block and you can do something with string s
}
</code></pre></li>
</ul>
<p>Remarque que la grammaire prend le temps de définir la notion de <em>« introduce by »</em> (section 14.30).</p>
<h2><em>Records</em></h2>
<p>Un enregistrement (<em>record</em>) (<a href="https://openjdk.java.net/jeps/395">JEP395</a>) est une classe représentant, sans <em>bla bla</em>, des données immuables. Là où l'on reproche à Java d'être trop verbeux, les <em>records</em> répondent présents.</p>
<p>Prenons l'exemple classique d'une classe représentant un <code>Point</code> dans le plan. Elle ressemblerait probablement à ceci (et pourrait être presque complètement générée par un IDE) : </p>
<pre><code class="language-java">
class Point {
private final int x;
private final int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
int getX() { return x; }
int getY() { return y; }
@Override
public boolean equals(Object o) {
if (!(o instanceof Point other)) return false;
return other.x == x && other.y == y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@Override
public String toString() {
return String.format("Point[x=%d, y=%d]", x, y);
}
}
</code></pre>
<p>Avec un <em>record</em>, il suffit d'écrire :</p>
<pre><code class="language-java">
record Point(int x, int y) {}
</code></pre>
<p><em>C'est plus concis.</em></p>
<ul>
<li><p>Sans constructeur explicite dans l'enregistrement (<em>record</em>), on ne dit pas dans la classe puisque ce n'en n'est pas une à proprement parler, un constructeur canonique (<em>canonical constructor</em>) existe. Un constructeur canonique assigne la valeur des paramètres aux attributs (comme le constructeur <code>Point</code> ci-dessus).</p>
<p>À ne pas confondre avec un constructeur par défaut qui initialise les attributs à zéro.</p></li>
<li><p>Les méthodes <code>equals</code>, <code>hashCode</code> et <code>toString</code> sont automatiquement réécrites.</p></li>
<li><p>Les accesseurs existent et portent le même nom que l'attribut (pas de <em>get</em> devant donc).</p></li>
<li><p>Si nécessaire, il est possible d'adapter le constructeur. Par exemple (extrait de <a href="https://openjdk.java.net/jeps/395">JEP395</a>) :</p>
<pre><code class="language-java">
record Range(int lo, int hi) {
Range {
if (lo > hi)
throw new IllegalArgumentException(
String.format("(%d,%d)", lo, hi));
}
}
</code></pre>
<ul>
<li>notons qu'il est inutile de réécrire <code>this.lo = lo…</code></li>
</ul></li>
</ul>
<p>Tous les <em>records</em> héritent de <code>java.lang.Record</code>.</p>
<p>Quelques différences avec une classe normale :</p>
<ul>
<li><p>pas d'<code>extends</code> pour un <em>record</em>. Un <em>record</em> hérite toujours de <code>java.lang.Record</code> ; </p></li>
<li><p>un <em>record</em> est implicitement <code>final</code> et ne peut pas être abstrait (<code>abstract</code>). L'idée étant qu'un enregistrement représente un état qui ne peut être modifié par la suite ; </p></li>
<li><p>les attributs du <em>record</em> sont <code>final</code>. Les <em>records</em> sont immuables par défaut ;</p></li>
<li><p>pas d'attributs statiques (<em>instance fields</em>) ni de bloc d'initialisation (<em>instance initializers</em>)_; </p></li>
<li><p>toute déclaration explicite d'un membre qui serait généré par défaut (accesseurs, <code>equals</code>,…), doit correspondre en type et bien préserver la sémantique du <em>record</em> ;</p></li>
<li><p>un <em>record</em> ne peut pas déclarer de méthodes natives (<em>native method</em>) qui impliquerait, par définition, que le <em>record</em> devienne dépendant d'un état externe.</p></li>
</ul>
<p>… et quelques similitudes : </p>
<ul>
<li>les instances sont créées <em>via</em> <code>new</code> ; </li>
<li>un <em>record</em> peut être déclarée <em>top level</em> ou interne (<em>nested</em>) et peut-être générique (<em>generic</em>) ; </li>
<li>un <em>record</em> peut déclarer des méthodes statiques (« de classe », <em>static method</em>), attributs statiques (<em>static field</em>) et bloc d'initialisation (<em>static initializer</em>) ;</li>
<li>un <em>record</em> peut également déclarer des méthodes d'instances (<em>instance method</em>) ;</li>
<li><p>un <em>record</em> peut implémenter des interfaces.</p>
<p>A record class can implement interfaces. A record class cannot specify a superclass since that would mean inherited state, beyond the state described in the header. A record class can, however, freely specify superinterfaces and declare instance methods to implement them. Just as for classes, an interface can usefully characterize the behavior of many records. The behavior may be domain-independent (e.g., Comparable) or domain-specific, in which case records can be part of a sealed hierarchy which captures the domain (see below).</p></li>
<li><p>un <em>record</em> peut déclarer des types internes y compris des classes enregistrements internes (<em>nested record classes</em>). Si une classe enregistrement est elle même interne, elle est implicitement <code>static</code> ;</p></li>
<li><p>il peut y avoir des annotations dans une classe enregistrement (<em>record</em>) ;</p></li>
<li><p>les instances peuvent être sérialisées et désérialisées. Cependant, ce sont les attributs qui déterminent la sérialisation et le constructeur canonique, la désérialisation. Il n'est pas question d'ajouter des méthodes comme <code>writeObject</code>, <code>readObject</code>, <code>readObjectNoData</code>, <code>writeExternal</code>, ou <code>readExternal</code>.</p></li>
</ul>
<h2><em>Unix-Domain Socket Channels</em></h2>
<p>Support des sockets Unix (<code>AF_UNIX</code>) et <em>server socket</em>(<a href="https://openjdk.java.net/jeps/380">JEP380</a>) utilisés dans les communications inter-processus (IPC, <em>inter-process communication</em>) sur un hôte. Ces sockets sont semblables aux sockets TCP/IP (<code>AF_INET</code>) mais ne reposent pas sur IP et utilisent un nom de fichier.</p>
<p>Ce support se fait par, entre autre, l'ajout de la classe<br />
<code>java.net.UnixDomainSocketAddress</code> à l'API et d'une valeur <code>UNIX</code> à l'énumération <code>java.net.StandardProtocolFamily</code>. Voir <a href="https://openjdk.java.net/jeps/380">JEP380</a>.</p>
<h2><em>Warning for value-based classes and designate primitive wrapper classes as value-based</em></h2>
<p>Les classes englobantes pour les types primitifs (<em>primitive wrapper classes</em>) comme <code>java.lang.Integer</code> deviennent des <em>value-based</em> classes (<a href="https://openjdk.java.net/jeps/390">JEP390</a>) ce qui entraine que leurs constructeurs sont dépréciés (ils l'étaient depuis longtemps) et seront supprimés dans une future <em>release</em>.</p>
<p>Une <em>value-based</em> classe (voir la <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/doc-files/ValueBased.html">définition chez Oracle</a>) a les propriétés suivantes :</p>
<ul>
<li>tous les attributs sont constants (<em>final</em>) ;</li>
<li>les implémentation de <code>equals</code>, <code>hashCode</code> et <code>toString</code> se basent uniquement sur la valeur des attributs ;</li>
<li>les méthodes de la classe traitent les instances comme librement échangeables (<em>freely substituable</em>) lorsqu'elles sont égales (au sens <em>equals</em>). Donc, échanger deux instances qui sont égales (<em>equals</em> et donc pas spécialement <em>==</em>) ne produit aucun changement visible dans le comportement de la méthode ;</li>
<li>la classe ne fait aucune synchronisation ; </li>
<li>la classe n'a pas de constructeurs accessibles. (Par exemple <code>java.lang.Integer</code> va perdre ses constructeurs au profit de <code>Integer.valueOf(int)</code> et <code>Integer.parseInt(String)</code>) ; </li>
<li>la classe ne propose aucun mécanisme d'instanciation garantissant qu'une instance retournée soit unique, par exemple une méthode <em>factory</em> appelée deux fois et retournant deux fois la même valeur (au sens <em>equals</em>) peut retourner deux valeurs identiques (au sens <em>==</em>) ; </li>
<li>la classe est <code>final</code> et hérite de <code>Object</code> ou d'une hiérarchie d'objects ne contenant que des classes abstraites ne déclarant aucun attributs ni <em>instance initializers</em> et dont les constructeurs sont vides.</li>
</ul>
<h2><em>Packaging tool</em></h2>
<p>Ajout d'une nouvelle commande <code>jpackage</code> fournissant le nouvel outil de <em>packaging</em> Java.</p>
<p>Pour fournir une application, il n'est pas suffisant de fournir un <code>jar</code> qu'il faudrait placer <em>à l'endroit qui va bien</em> et ayant accès aux librairies éventuelles. <code>jpackage</code> permet de diffuser un paquet installable pour la plateforme cible (Linux, MS Windows, mac OS).</p>
<p>Par exemple une commande à l'allure suivante :</p>
<p></code></pre>bash
jpackage --name myapp --input lib --main-jar main.jar
</code></pre></p>
<p>À ceci s'ajoutent :</p>
<ul>
<li>la migration du dépôt d'OpenJDK de Mercurial vers Github ;</li>
<li>la seconde <em>preview</em> des classes scellées (voir JDK17 ci-dessous) ;</li>
<li>…</li>
</ul>
<h1>JDK 17</h1>
<h2>Sealed class</h2>
<p>Java 17 introduit les <strong>classes et interfaces scellées</strong> (<em>sealed class and interfaces</em>). Cette introduction fait suite (comme d'habitude maintenant) à deux <em>preview</em> dans le JDK16 et JDK15. Ces classes et interfaces restreignent la possibilité pour d'autres classes ou interfaces d'en hériter ou de les implémenter.</p>
<p>Un classe scellée peut être étendue uniquement par les classes explicitement autorisées. De même pour les interfaces et l'implémentation. Il suffit d'énoncer les classes avec leur « petit nom » si l'on est dans le même <em>package</em> ou avec le nom longs sinon.</p>
<p>Par exemple (issu de <a href="https://openjdk.java.net/jeps/409">JEP409</a>) :</p>
<pre><code class="language-java">
package com.example.geometry;
public abstract sealed class Shape
permits Circle, Rectangle, Square {
// write some code
}
</code></pre>
<p>Avant les classes scellées, les seules manières de limiter l'héritage ou l'implémentation étaient</p>
<ul>
<li>l'usage de <code>final</code> pour empêcher complètement l'héritage ;</li>
<li>limiter la visibilité de la classe ou des constructeurs aux packages pour n'autoriser l'héritage ou l'implémentation qu'aux packages.</li>
</ul>
<p>Il est possible d'omettre le mot clé <code>permits</code> lorsque l'on définit les classe autorisées directement dans la classe scellée. Dans l'exemple suivant (issu de <a href="https://openjdk.java.net/jeps/409">JEP409</a>), la classe <code>Root</code> est scellée et n'autorise que les classes <code>A</code>, <code>B</code> et <code>C</code>.</p>
<pre><code class="language-java">
abstract sealed class Root { ...
final class A extends Root { ... }
final class B extends Root { ... }
final class C extends Root { ... }
}
</code></pre>
<p>Une classes scellée (<em>sealed class</em>) impose trois contraintes :</p>
<ol>
<li><p>la classe scellée et ses sous-classes autorisées doivent se trouver dans le même module et, si le module n'est pas nommé, dans le même package ;</p></li>
<li><p>chaque sous-classe autorisée doit étendre directement la classe scellée ;</p></li>
<li><p>chaque sous-classe autorisée doit utiliser un <em>modifier</em> précisant comment se propage le sceau :</p>
<ul>
<li>elle peut être déclarée <code>final</code> et l'héritage s'arrête là. (Pour rappel, un <code>Record</code> est implicitement <code>final</code>) ; </li>
<li>elle peut être déclarée <code>sealed</code> et permettre l'héritage mais restreint ; </li>
<li>elle peut enfin être déclarée <code>non-sealed</code> et permettre l'héritage en enlevant le sceau pour les classes sous elle. Une classe scellée ne peut empêcher l'un de ses enfants d'enlever son sceau. </li>
</ul></li>
</ol>
<p>Ces trois <em>modifiers</em> sont évidemment exclusifs. Voici l'exemple de <a href="https://openjdk.java.net/jeps/409">JEP409</a> illustrant ces 3 situations :</p>
<pre><code class="language-java">
package com.example.geometry;
public abstract sealed class Shape
permits Circle, Rectangle, Square, WeirdShape {
// some code
}
public final class Circle extends Shape {
// some code
}
public sealed class Rectangle extends Shape
permits TransparentRectangle, FilledRectangle {
// some code
}
public final class TransparentRectangle extends Rectangle {
// some code
}
public final class FilledRectangle extends Rectangle {
// some code
}
public final class Square extends Shape {
// some code
}
public non-sealed class WeirdShape extends Shape {
// some code
}
</code></pre>
<p>L'utilisation est tout à fait semblable pour les interfaces</p>
<pre><code class="language-java">
package com.example.celestial;
sealed interface Celestial
permits Planet, Star, Comet { ... }
final class Planet implements Celestial { ... }
final class Star implements Celestial { ... }
final class Comet implements Celestial { ... }
</code></pre>
<p>et pour les classes enregistrements (<em>records</em>)</p>
<pre><code class="language-java">
package com.example.expression;
public sealed interface Expr
permits ConstantExpr, PlusExpr, TimesExpr, NegExpr { ... }
public record ConstantExpr(int i) implements Expr { ... }
public record PlusExpr(Expr a, Expr b) implements Expr { ... }
public record TimesExpr(Expr a, Expr b) implements Expr { ... }
public record NegExpr(Expr e) implements Expr { ... }
</code></pre>
<h3><em>Sealed classes and pattern matching</em></h3>
<p>Les classes scellées vont <em>bien se mettre</em> avec le <em>pattern matching</em> (j'avais dit que l'on en reparlerait !). JDK17 propose en <em>preview</em> le <em>pattern matching</em> pour le <code>switch</code> (<em>pattern matching for switch</em>). L'idée est d'étendre les types autorisés dans un switch (actuellement les types numériques, les énumérations et les <code>String</code>) à d'autres « choses » ayant un nombre limité et connu de valeurs… et c'est le cas des classes scellées puisque le nombre d'enfants est fixés (même si le nombre de petit-enfants ne l'est pas).</p>
<p>En reprenant l'exemple des formes ci-dessus, on pourrait actuellement écrire ceci (l'exemple est un peu discutable, il n'utilise pas de <em>pattern matching</em> pour <code>instanceof</code>… qui ne serait pas très utile puisque le polymorphisme fait le boulot pour <code>Rectangle</code> et <code>Square</code>) :</p>
<pre><code class="language-java">
Shape rotate(Shape shape, double angle) {
if (shape instanceof Circle) return shape;
else if (shape instanceof Rectangle) return shape.rotate(angle);
else if (shape instanceof Square) return shape.rotate(angle);
else throw new IncompatibleClassChangeError();
}
</code></pre>
<p>Le compilateur ne peut être sûr que les tests (<code>if</code>) couvrent toutes les sous-classes de <code>Shape</code>. Le dernier <code>else</code> est inatteignable mais le compilateur ne peut pas le savoir et s'il « manque » un test, ce ne sera pas détecté.</p>
<p>Si la <em>preview</em> du <em>pattern matching for switch</em> est maintenue, on pourra écrire :</p>
<pre><code class="language-java">
Shape rotate(Shape shape, double angle) {
return switch (shape) { // pattern matching switch
case Circle c -> c;
case Rectangle r -> r.rotate(angle);
case Square s -> s.rotate(angle);
// no default needed!
}
}
</code></pre>
<p>… mais c'est une autre histoire…</p>
<p>À ceci s'ajoutent :</p>
<ul>
<li>une nouvelle implémentation des générateurs de nombres pseudoaléatoires (<a href="https://openjdk.java.net/jeps/356">JEP356</a>) ; </li>
<li>l'API <code>Applet</code> est dépréciée et appelée à être retirée. Les fournisseurs de navigateurs internet (<em>web browsers</em>) ayant retirés, ou annoncés que ce serait fait, le support des applets Java. S'en rappelle-t-on d'ailleurs ? </li>
<li>la suppression annoncée de plusieurs classes dépréciées. </li>
</ul>
<p>Et là, on a fait le tour de certains changements et nouvelles fonctionnalités de la version 12 à 17… en ± 3 ans. En espérant que ça vous aide…</p>
<p><br/></p>
<p><em>Crédit photo perso au détour d'une balade</em></p>
Envoyer les mails pendant les heures du bureau : fausse bonne idée.urn:md5:214e8be5dd1f59d79aa3ae4b1cc162cc2021-04-11T12:29:00+02:002021-04-11T11:48:44+02:00PiTCartable au dosenseignementesi<p><img src="https://blog.namok.be/public/images/divers/2021/cabine-53e1cb8a35702004f7dc5301.jpg" alt="cabine-53e1cb8a35702004f7dc5301.jpg, avr. 2021" style="margin: 0 auto; display: block;" title="une ancienne cabine téléphonique" /></p>
<p>Je lisais dans le <a href="https://www.lacsc.be/csc-enseignement/publications/csc-educ/csc-educ-2020-2021">CSC-EDUC 148</a> d'avril 2021 un article sur le droit à la déconnexion.</p>
<p>(<em>Je me permets de vous le partager comme si je vous le prêtais dans la salle des profs ou si je le laissais trainer dans le train après l'avoir lu. <a href="https://blog.namok.be/public/documents/csc-educ-148.pdf">[PDF]</a></em>)</p>
<p>Poser un cadre pour la communication électronique <em>prof-prof</em>, <em>prof-direction</em>
et <em>prof-élève</em> ou <em>prof-étudiant.e</em> est une bonne idée. La proposition
s'appelle <strong>Garantir un droit à la déconnexion pour chaque membre du
personnel</strong>. Même si j'aurais préféré lire un titre comme : <strong>Cadre pour la
communication électronique entre les différentes personnes intervenant dans
l'école</strong> parce que la communication va dans les deux sens et que je trouve que
nous avons aussi un <em>devoir de connexion</em>, il est important de poser cette
question.</p>
<p>La position de la CSC me semble sensée et pas surprenante. Bien sûr, j'y lis un
rappel bienvenu de la différence entre <em>répondre</em> et <em>prendre connaissance</em> mais
je ne m'attarderais que sur un seul point qui me semble être une fausse bonne
idée. Il s'agit de :</p>
<blockquote>
<p>Obliger l'envoi des mails pendant les heures de travail grâce à la fonction d'envoi différé.</p>
</blockquote>
<p><a href="https://blog.namok.be/public/images/divers/2021/csc-educ-148-extrait.png" title="csc-educ-148-extrait.png, avr. 2021"><img src="https://blog.namok.be/public/images/divers/2021/csc-educ-148-extrait.png" alt="csc-educ-148-extrait.png, avr. 2021" style="margin: 0 auto; display: block;" /></a></p>
<p>Premièrement, cette proposition est <em>GAFAM-webmail-centrée</em>.</p>
<p>Sans préjuger de l'intention des rédacteurices, les clients <em>gmail</em> et <em>outlock</em>
proposent la fonctionnalité d'envoi mais plein d'autres non. Par exemple ;
<em>Thunderbird_<sup id="fnref:f1"><a href="https://blog.namok.be/?post/2021/04/11/envoyer-les-mails-pendant-les-heures-du-bureau-fausse-bonne-idee#fn:f1" rel="footnote">1</a></sup>, Sogo, Roundcube<sup id="fnref:f2"><a href="https://blog.namok.be/?post/2021/04/11/envoyer-les-mails-pendant-les-heures-du-bureau-fausse-bonne-idee#fn:f2" rel="footnote">2</a></sup>, Nextcloud (avec le _plugin</em> mail)…
Cette proposition est donc trop réductrice. Si j'étais d'humeur chagrine, je
dirais qu'elle m'impose l'utilisation d'un outil particulier pour lire et écrire
mes mails.</p>
<p>Ma deuxième remarque n'est pas technique. Le mail est asynchrone depuis toujours.</p>
<p>J'écris et je lis mes mails <strong>quand je veux</strong>, <strong>quand ça m'arrange</strong> et mæ
interlocuteurice aussi (cfr. <a href="https://blog.namok.be/?post/2016/12/19/bon-anniversaire-netiquette">Netiquette
2.0</a> 10 et
25). Je ne vois aucune raison d'obliger l'envoi du mail pendant les heures de
travail.</p>
<p>La plupart d'entre nous avons choisi l'enseignement pour la souplesse que le
métier offre dans le choix des périodes de travail. Par exemple, c'est tellement
pratique lorsque la personne à qui j'envoie ma question à 23h a l'envie de me
répondre à 6h pendant que je dors, ce qui me permettra d'avancer sur le boulot
pendant qu'elle fait une balade au bois l'après-midi. Ce serait dommage de se
priver de cette souplesse.</p>
<p>Je pratique <em>inbox zéro</em> c'est-à-dire la partie <em>inbox</em> (mails entrants) de ma
boite mail contient peu de mails (en général moins de 15). Je fonctionne plus ou moins comme ceci : à la réception d'un mail</p>
<ul>
<li>si c'est rapide, je le traite ; </li>
<li>si c'est long, je le laisse dans mon <em>inbox</em> jusqu'au traitement ; </li>
<li>si « ça fait longtemps qu'il est là », je ne le traiterai pas et j'archive. L'expéditeurice reviendra vers moi si c'est important. (Je n'ai pas souvent besoin de le faire).</li>
</ul>
<p>Je n'ai donc pas envie d'être assailli par moult mails le lundi à 8h alors que
j'aurais pu traiter certaines demandes rapides au fil des heures du week-end par
exemple. Si j'avais voulu tout traiter le lundi à 6h parce que je suis un
lève-tôt et que je préfèrais que ce soit fait avant mon cours de 8h… perdu. Je
ne peux pas.</p>
<p>J'ai l'impression — toujours sans jugement — que cette proposition est faite par
des personnes hyper-connectées ou gérant mal leurs notifications. Je ne reçois
<strong>aucune notification</strong> de ma boite mail professionnelle. Je ne vois donc mes
mails professionnels que lorsque <strong>je décide de consulter mes mails</strong>. Avec le
mail, je me déconnecte donc quand je veux.</p>
<p>Le mail correctement géré permet la déconnexion.</p>
<p><br/></p>
<p><em>Crédit photo <a href="https://www.lalibre.be/regions/bruxelles/les-cabines-telephoniques-vivent-leurs-derniers-instants-53e13fb43570667a6390ee7f">La libre</a>.</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Il y a bien un plugin qui enverra le mail au moment demandé (<strong>si ma machine est allumée</strong>) mais qui ne fonctionne pas/plus avec ma version de Thunderbird. <a href="https://blog.namok.be/?post/2021/04/11/envoyer-les-mails-pendant-les-heures-du-bureau-fausse-bonne-idee#fnref:f1" rev="footnote">↩</a></p>
</li>
<li id="fn:f2">
<p>Il semble exister un plugin payant. <a href="https://blog.namok.be/?post/2021/04/11/envoyer-les-mails-pendant-les-heures-du-bureau-fausse-bonne-idee#fnref:f2" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Un voile et un t-shirturn:md5:ffb30fbadcec8db692255c82af1a48cb2021-01-16T16:21:00+01:002021-01-16T16:25:36+01:00PiTCartable au dosenseignementesipolitique<p><img src="https://blog.namok.be/public/images/divers/2021/lescapricesdiris.com.jpg" alt="lescapricesdiris.com.jpg, janv. 2021" style="margin: 0 auto; display: block;" /></p>
<p>Je pars pour l'école avec mon t-shirt <em>Tux</em>.</p>
<p>Je suis toujours un peu stressé quand j'arbore ce t-shirt représentant la
mascotte linux. D'ailleurs en entrant dans l'école, je me fais interpeller :</p>
<p>— Que signifie ce pingouin sur ton t-shirt ?<br />
— Ben, ce n'est pas un pingouin, c'est un manchot et c'est simplement un t-shirt
que j'aime bien.<br />
— Ok. C'est bon.</p>
<p>Je sais très bien ce que je dois répondre.</p>
<p>Il y a quelque jours, mon pote est arrivé à l'école avec un t-shirt montrant <em>un
tux avec une tapette à mouche voulant écraser un papillon aux couleurs window</em>
et il a du rentrer chez lui parce qu'il est interdit de se « permettre toute
publicité ou toute propagande pour un mouvement politique, philosophique ou
religieux ». Comme il avait répondu que « windows c'est de la m°@$e » (<em>sic</em>),
on lui a dit que c'était de la propagande philosophique et qu'il contrevenait au
règlement des études. Il a donc du rentrer se changer.</p>
<p><em>Fin de la fiction.</em></p>
<p><a href="https://www.lalibre.be/belgique/enseignement/le-voile-sera-massivement-autorise-en-septembre-dans-l-enseignement-superieur-l-interet-general-doit-primer-6001eabb9978e227df936a0d">À la rentrée de septembre 2021, les signes convictionnels seront autorisés dans
les écoles supérieures dépendantes de WBE (Wallonie-Bruxelles
Enseignement)</a>. Les filles qui le désirent pourront porter le voile
islamique. Par extension — même si l'article ne le dit pas — d'autres signes
convictionnels pourront être portés. Il sera donc normalement possible de porter
une kippa, un turban, une croix ou encore le crâne rasé et une tenue orange…</p>
<p>La règle est assez bien respectée et je peux compter sur le doigt (<em>si si</em>) d'une main le nombre de fois où
il a fallu rappeler que le port du voile était interdit dans l'enceinte de l'école.</p>
<p>Évidemment, personne ne s'est vu refuser l'entrée de l'école pour t-shirt
inapproprié. Si je peux afficher mes convictions religieuses, j'estime pouvoir
afficher mes convictions aussi simples qu'elles soient. Plus d'inquiétude pour
les t-shirt avec un message. Nous voilà rassurés. Me voilà rassuré.</p>
<p>Outre les t-shirt « <em>de geek</em> » qui sont plus souvent des t-shirts de <em>nerds</em>
pour ma part, ce sera <em>open bar</em> pour vos messages convictionnels. Vous pouvez
sortir vos t-shirts : <em>Che Guevara</em>, 100% bio, Non à la THT, Le nucléaire tue, I
love N-VA, La terre est plate, 5G non merci, BÉPO > AZERTY…</p>
<p>Je m'en vais de ce pas refaire un stock de t-shirts en attendant le décret.</p>
<p><br/></p>
<p><em>Crédit photo par <a href="http://www.lescapricesdiris.com/look-geek/">Iris</a>. Je cherchais une photo d'une fille avec un t-shirt de geek. Je ne trouvais pas puis, j'ai trouvé <a href="http://www.lescapricesdiris.com/look-geek/">Iris</a></em></p>
tmux à l'usage d'un profurn:md5:f155fb22d2c87d3ed06de5e04a0a573a2020-12-03T14:14:00+01:002020-12-03T20:19:43+01:00PiTMes doigts dans le clavieresiinutilelinuxlogiciellibre<p><img src="https://blog.namok.be/public/images/divers/2020/cadres_by_thoum_d2458vb.jpg" alt="cadres_by_thoum_d2458vb.jpg, déc. 2020" style="margin: 0 auto; display: block;" /></p>
<p><a href="https://github.com/tmux/tmux/wiki"><code>tmux</code></a> est un multiplexer de terminal — oui, ce billet est un tout petit
peu technique — permettant d'ouvrir une session sur une machine et de s'en
détacher en laissant la session ouverte (un peu comme <code>screen</code>).</p>
<p>Dans l'usage quotidien<sup id="fnref:f1"><a href="https://blog.namok.be/?post/2020/12/03/tmux-a-l-usage-d-un-prof#fn:f1" rel="footnote">1</a></sup> de ma machine, j'utilise régulièrement un terminal
et j'ai un répertoire par « activité » : un répertoire pour tel cours, un
répertoire pour telle organisation… Un peu comme tout le monde j'imagine excepté
les personnes qui <em>mettent tout sur le bureau</em> !</p>
<p>Lorsque je me consacre à un cours par exemple, je peux être dans le répertoire
du cours pour trouver un énoncé, dans un autre terminal, être dans le répertoire
pour les corrections… j'utilise donc plusieurs terminaux pour un même cours.
Comme je travaille tantôt sur un cours, tantôt sur un autre cours et que j'ai
plusieurs bureaux virtuels, ça devient vite désorganisé.</p>
<p>Pendant tout un temps, je retenais que le premier bureau, c'est pour tel cours,
le second pour un autre, le cinquième pour les navigateurs internet, le sixième
pour la communication instantanée et mail, etc. Depuis quelques temps,
j'utilise <code>tmux</code> pour les terminaux et je conserve cette habitude de laisser
dans mon bureau virtuel 6 le mail et irc et dans le 5, les navigateurs internet.</p>
<p><code>tmux</code> me permet d'ouvrir une session par cours. Dans cette session, je peux
ouvrir plusieurs terminaux. Les intérêts de <code>tmux</code> sont pour moi :</p>
<ul>
<li>la possibilité de pouvoir me détacher et attacher une session. Ce qui me
permet de ne plus être lié à un bureau virtuel pour une activité ;</li>
<li>la possibilité de <em>découper</em> ma fenêtre en plusieurs ;</li>
<li>…</li>
</ul>
<p>Bref, voici mon aide-mémoire de commandes.</p>
<p><code>tmux</code> lance une session (numérotée) mais c'est mieux de directement la nommer
avec l'option <code>-t</code> :</p>
<pre><code class="language-">
tmux new -t <name>
</code></pre>
<p>Pour lister les sessions existantes :</p>
<pre><code class="language-">
tmux ls
</code></pre>
<p>Pour rejoindre — s'attacher à — une session existante :</p>
<pre><code class="language-bash">
tmux a [-t <name>]
tmux a [-t <number>]
</code></pre>
<p>Pour tuer toutes les sessions <code>tmux</code>, il suffit de tuer le serveur. Il est aussi
possible de n'en tuer qu'une par son nom ou son numéro :</p>
<pre><code class="language-">
tmux kill-server
tmux kill-session -t <name>
tmux kill-session -t <number>
</code></pre>
<p>Dès lors que l'on se trouve dans une session <code>tmux</code>, un préfixe défini permet de
lancer une commande. <code>Ctrl-b</code> est le préfixe par défaut. Dans la suite, <em>prefix</em>
signifie chez moi <code>Ctrl-b</code>. Vous pourrez le redéfinir mais pour moi — en bépo —
c'est accessible.</p>
<ul>
<li><em>prefix</em> <code>d</code> se détache de la session courante ;</li>
<li><em>prefix</em> <code>:</code> permet d'écrire une commande <em>tmux</em> ;</li>
<li><em>prefix</em> <code>$</code> (re)nomme la session courante ;</li>
<li><em>prefix</em> <code>)</code> passe une autre session (la suivante « à droite ») ;</li>
<li><em>prefix</em> <code>(</code> passe une autre session (la suivante « à gauche ») ;</li>
<li><em>prefix</em> <code>s</code> liste les sessions (comme <code>tmux ls</code> mais en étant attaché) et
permet de passer d'une session à l'autre ;</li>
</ul>
<h3>Fenêtre</h3>
<p>Une session <code>tmux</code> se compose d'une ou plusieurs fenêtres. Une fenêtre occupe
tout l'écran du terminal.</p>
<ul>
<li><em>prefix</em> <code>,</code> renommer la fenêtre ;</li>
<li><em>prefix</em> <code>c</code> créer une nouvelle fenêtre ;</li>
<li><em>prefix</em> <code>n</code> aller à la fenêtre suivante ;</li>
<li><em>prefix</em> <em>X</em> aller à la fenêtre <em>X</em> ;</li>
<li><em>prefix</em> <code>w</code> liste les fenêtres et permet de les parcourir et d'en choisir
une ;</li>
<li><em>prefix</em> <code>esperluette</code> supprime la fenêtre courante (et tous ses panels) ;</li>
</ul>
<h3>Panels</h3>
<p>Une fenêtre peut être découpée en <em>morceaux</em> / carrés / <em>panels</em>.</p>
<ul>
<li><em>prefix</em> <code>%</code> scinde le panel en deux verticalement ;</li>
<li><em>prefix</em> <code>=</code> scinde le panel en deux horizontalement (par défaut c'est <code>"</code>
mais j'ai changé) ;</li>
<li><em>prefix</em> <em>flèche</em> passe d'un panel à l'autre dans la direction de la flèche ;</li>
<li><em>prefix</em> <code>Ctrl-</code><em>flèche</em> redimensionne le panel courant dans le sens de la
flèche ;</li>
<li><em>prefix</em> <code>o</code> échange les panels ;</li>
<li><em>prefix</em> <code>;</code> retourne au dernier panel ;</li>
<li><em>prefix</em> <code>x</code> supprime le panel courant</li>
</ul>
<p>Il est possible d'exécuter les mêmes actions dans tous les panels en même temps.
Pour ce faire, entrer la commande <code>:setw synchronize-panes on</code> (et <code>off</code> quand
c'est terminé).</p>
<p>Ensuite, on s'amuse à écrire un fichier de configuration en fonction des
habitudes que l'on a. Le <a href="https://github.com/Pinkilla/dotfiles">mien se trouve là</a>.</p>
<ul>
<li>quand je ferme un <em>pane</em> ou une fenêtre, je n'ai pas besoin de confirmer ;</li>
</ul>
<pre><code class="language-">
# kill pane without confirm
bind-key x kill-pane
# new window in some directory
bind-key c new-window -c "#{pane_current_path}"
</code></pre>
<ul>
<li>je redéfinis le <em>split</em> horizontal en demandant d'ouvrir le nouveau panel dans
le répertoire courant ;</li>
</ul>
<pre><code class="language-">
# split window verticaly and horizontaly
bind-key = split-window -v -c "#{pane_current_path}"
bind-key % split-window -h -c "#{pane_current_path}"
</code></pre>
<ul>
<li>j'augmente la taille du buffer pour l'historique ;</li>
</ul>
<pre><code class="language-">
# big history (default is 2000)
set -g history-limit 5000
</code></pre>
<p>Voilà, pour le reste, nous verrons à l'usage…</p>
<p><br/></p>
<p><em>Crédit photo chez DeviantArt par <a href="https://www.deviantart.com/thoum/art/Cadres-127895591">Thoum</a></em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Je sais que le mot est un peu tombé en désuétude au profit de
<em>journalier</em>… que je trouve beaucoup moins joli. Oui les mots peuvent être
beaux. <a href="https://blog.namok.be/?post/2020/12/03/tmux-a-l-usage-d-un-prof#fnref:f1" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Monitoring Moodle : script pour telegraf et dashboard pour Grafanaurn:md5:72e385aed047331a76549a8b1f70fb682020-06-07T16:21:00+02:002020-10-07T07:50:08+02:00PiTMes doigts dans le clavierdebianenseignementesigeeklogiciellibremoodle<p><img src="https://blog.namok.be/public/images/divers/2020/xiaolong-wong-9gpERFdyDW4-unsplash-1200x.jpg" alt="xiaolong-wong-9gpERFdyDW4-unsplash-1200x.jpg, juin 2020" style="margin: 0 auto; display: block;" /></p>
<p>Pour monitorer la machine et le service de <em>elearning</em> <strong>Moodle</strong>, il existe
moult manières de faire. L'une d'entre elles est d'utiliser la pile
<strong>telegraf-InfluxDB-Grafana</strong> :</p>
<ul>
<li><em>telegraf</em> pour la récolte d'informations sur la machine faisant tourner
<em>moodle</em> ;</li>
<li><em>InfluxDB</em> pour stocker ses informations et ;</li>
<li><em>Grafana</em> pour l'aspect visuel et les beaux graphiques. </li>
</ul>
<p><a href="https://blog.namok.be/public/images/divers/2020/Firefox_Screenshot_2020-06-07T08-56-27.765Z.png" title="Firefox_Screenshot_2020-06-07T08-56-27.765Z.png, juin 2020"><img src="https://blog.namok.be/public/images/divers/2020/.Firefox_Screenshot_2020-06-07T08-56-27.765Z_m.png" alt="Firefox_Screenshot_2020-06-07T08-56-27.765Z.png, juin 2020" style="margin: 0 auto; display: block;" /></a></p>
<p><a href="https://blog.namok.be/public/images/divers/2020/Firefox_Screenshot_2020-06-07T09-01-08.267Z.png" title="Firefox_Screenshot_2020-06-07T09-01-08.267Z.png, juin 2020"><img src="https://blog.namok.be/public/images/divers/2020/.Firefox_Screenshot_2020-06-07T09-01-08.267Z_m.png" alt="Firefox_Screenshot_2020-06-07T09-01-08.267Z.png, juin 2020" style="margin: 0 auto; display: block;" /></a></p>
<p>L'objet de cet article est <em>l'affichage du nombre d'utilisateurs et
d'utilisatrices connectées</em> à la plateforme et, en cadeau, le <em>dashboard</em> que
j'utilise pour <em>Moodle / PostgreSQL</em>.</p>
<h3>Aller chercher le nombre d'utilisateurs et utilisatrices connectées et remplir la BD</h3>
<p>À ma connaissance, il n'y a pas de moyen d'obtenir le nombre de personnes
connectées au temps <em>t</em> mais bien entre le temps <em>t1</em> et <em>t2</em>. Comme je n'ai pas
trouvé évident d'utiliser l'API pour ce faire, suivant <a href="https://moodle.org/mod/forum/discuss.php?d=404772">un conseil sur le forum
Moodle</a>, j'ai choisi d'aller chercher l'info dans la base de données
(BD).</p>
<p>C'est dans la table <code>mdl_user</code> que se trouvent les informations de connections.
Il suffit de compter les personnes qui se sont connectées « avant maintenant »
et « après maintenant moins un peu de temps ».</p>
<p>Un script <code>bash</code> pourrait ressembler à :</p>
<pre><code class="sh">
$ cat moodleusers.sh
#!/bin/bash
timestamp_now=$(date --utc +"%s")
timestamp_start=$(($timestamp_now - 60))
QUERY="SELECT COUNT(*) FROM mdl_user
WHERE deleted=0 AND lastaccess > "${timestamp_start}"
AND lastaccess < "${timestamp_now}" ;"
RESULT=$(psql -d 'moodle' -c "${QUERY}" 2>/dev/null)
RESULT_CUT=$(echo $RESULT | cut -d ' ' -f 3)
echo "nUsersMoodle nUsersMoodle=${RESULT_CUT}"
</code></pre>
<ul>
<li>les <em>timestamps</em> sont donnés en GMT ;</li>
<li>le script affiche le résultat qui sera compréhensible par <em>InfluxDB</em></li>
<li>je ne suis pas parvenu à obtenir le résultat dans une seule variable (je ne
suis pas assez bon avec <em>bash</em>). j'ai donc fait <em>RESULT</em> puis <em>RESULT_CUT</em>.</li>
</ul>
<p>C'est <em>telegraf</em> qui va lancer le script. C'est donc l'utilisateur <code>telegraf</code>
qui devra avoir les droits de lecture dans la BD pour la table <code>mdl_user</code>. Ce
qui peut se faire comme suit :</p>
<pre><code class="sh">
# sudo -u postgres psql -d moodle
moodle =# create role telegraf
moodle =# alter role telegraf with login
moodle =# grant telegraf to moodle
moodle =# grant select on mdl_user to telegraf
</code></pre>
<p>Après vérification que le script est bien fonctionnel et que l'utilisateur
<code>telegraf</code> peut le lancer, il reste à l'ajouter à <code>telegraf</code> comme suit :</p>
<pre><code class="sh">
# vim /etc/telegraf/telegraf.conf
[[inputs.exec]]
# ## Commands array
commands = [
"/elsewhere/moodleusers.sh"
]
# ## Timeout for each command to complete.
timeout = "5s"
data_format = "influx"
interval = "60s"
</code></pre>
<ul>
<li>demander à <em>telegraf</em> de recharger son fichier de conf ;</li>
<li>vérifier que tout fonctionne bien ;</li>
<li>attendre un peu que la BD InfluxDB se remplisse. </li>
</ul>
<h3>Faire de beaux graphes</h3>
<p>Il reste à récupérer cette information via <em>Grafana</em> et l'inclure dans son
<em>dashboard</em>. J'en ai profité pour proposer <a href="https://grafana.com/grafana/dashboards/12418">un dashboard Moodle with
postgres</a>.</p>
<p>Nous verrons à l'usage, s'il est fonctionnel.</p>
<p>Je suis preneur de tous vos retours et corrections.
<em>Enjoy !</em></p>
<p><br/></p>
<p><em>Crédit photo par <a href="https://unsplash.com/photos/9gpERFdyDW4">Xiaolong Wong</a> chez Unsplash. Une orange bien découpée
fait un beau camembert.</em></p>
Le professeur ne nous a rien apprisurn:md5:d4ddbb1893de73616b17411c68a8da9d2020-04-14T10:25:00+02:002020-04-14T09:31:58+02:00PiTCartable au dosconfinementenseignementesiinutile<p><img src="https://blog.namok.be/public/images/divers/2020/nigel-cohen-gwpx9DOKB4w-unsplash-1200.jpg" alt="" style="margin: 0 auto; display: block;" /></p>
<p><em>La libre</em> publiait ce vendredi 10 avril 2020 une opinion de Thomas Ravanelli
intitulée : <a href="https://www.lalibre.be/debats/opinions/confinement-et-etudes-superieures-si-le-professeur-ne-nous-a-rien-appris-il-n-y-a-rien-a-evaluer-5e8f18409978e228415c3a18">Confinement et études supérieures : si le professeur ne nous a rien
appris, il n’y a rien à évaluer</a>.</p>
<p>Le titre est probablement choisi par <em>La libre</em> pour attirer le chaland mais il
se trouve cependant dans le texte et, avant la lecture de l'article, ce titre me
bloque.</p>
<p>– Le professeur ne nous a rien appris.</p>
<p>Mon métier — je suis enseignant et c'est pourquoi l'article me fait réagir —
n'est pas d'apprendre mais d'<strong>essayer de mettre en situation d'apprentissage</strong>.
D'ailleurs, n'appelle-t-on pas l'étudiant ou l'étudiante, l'apprenant
ou l'apprenante. Preuve, s'il en est, que c'est bien cette personne qui apprend.</p>
<p>Quand je lis cette phrase, je perçois un grand manque d'autonomie ou une
formulation malheureuse. La personne qui attend que le prof<sup id="fnref:f2"><a href="https://blog.namok.be/?post/2020/04/14/le-professeur-ne-nous-a-rien-appris#fn:f2" rel="footnote">1</a></sup> lui apprenne
est dans une position d'attente comme une oie que l'on gave. L'apprentissage est
différent du <em>binge-watching</em> où la personne est totalement passive et simple
réceptrice d'images, de sons, de transparents de cours, d'enregistrements vidéo
de séances, etc. Pour moi, apprendre est un <strong>processus actif</strong> : je lis, je
cherche, je demande, je trouve, je note, je résume, je reformule, je compulse,
j'écoute, j'essaie, je me trompe, je recommence, je dessine, etc. Je suis
conscient que nos étudiants et étudiantes de première année ne sont, pour la
plupart, pas ou peu prêtes<sup id="fnref:f1"><a href="https://blog.namok.be/?post/2020/04/14/le-professeur-ne-nous-a-rien-appris#fn:f1" rel="footnote">2</a></sup> à cette demande d'autonomie et nous faisons au
mieux pour les y préparer. Étape par étape et certains et certaines
enseignantes<sup id="fnref2:f1"><a href="https://blog.namok.be/?post/2020/04/14/le-professeur-ne-nous-a-rien-appris#fn:f1" rel="footnote">2</a></sup> du primaire – dans des écoles à pédagogie dite alternative
malheureusement trop rares — et du secondaire préparent les enfants à être
acteurs et actrices de leurs apprentissages et non spectatrices. Un acteur est
actif, un spectateur passif.</p>
<p><em>Ensuite, est arrivée la « grande hibernation » suite au COVID-19 2020</em>.</p>
<p>Les haute-écoles et universités ne sont pas « passées à un enseignement à
distance » absolu où tous les cours sont remplacés par du <em>elearning</em> ou des
<em>MOOC</em> préparés de longue date par des personnes formées à l'enseignement à
distance. Non, les haute-écoles et les universités essaient de proposer une
alternative temporaire à l'enseignement en présentiel. Elles essaient — c'est du
moins l'intention que je leur prête et je ne suis pas le seul — de ne pas
laisser tomber leurs étudiants et étudiantes et de leur permettre de réussir des
crédits afin de ne pas hypothéquer leurs études ni dévaloriser leur diplôme.</p>
<p>— J'ai été diplômé en 2020.<br />
— Ah oui, nous vous recontacterons.</p>
<p>La consigne reçue étant que « <a href="http://enseignement.be/index.php?page=28301&navi=4684">dans la mesure du possible, les modules de cours
sont organisés à distance. Ainsi, les activités d’apprentissage à distance
remplacent les activités d’apprentissage en présentiel qui sont
suspendues.</a> », la majorité des enseignants a réfléchi à la meilleure
manière de faire, avec plus ou moins de réussite en fonction des cours, des
établissements et des sensibilités de chaque personne. Nous sommes évidemment
conscients que le quotidien des étudiants et le nôtre a changé et qu'être « en
confinement » ne signifie pas disposer de davantage de temps à consacrer à
l'école. C'est peut-être se trouver dans un environnement moins adapté à la
ville ou à la campagne, trouver et partager des solutions informatiques, accéder
différemment à la documentation, s'occuper d'enfants, s'inquiéter et soutenir
les personnes fragiles ou non de son entourage, etc.</p>
<p>Alors j'essaie de mettre mes étudiants et étudiantes en situation
d'apprentissage de manière un peu différente mais peut-être pas tant que ça. Les
lectures proposées — syllabus, transparents, liens, etc. — restent identiques,
les réponses aux questions <em>via</em> <a href="http://namok.be/blog/?post/2020/04/14/fora-reborn">forum refont surface</a> — et c'est un excellent
apprentissage de formulation de questions que l'on avait un peu perdu —, les
réponses individuelles et les exposés oraux ont changé et c'est à la fois
difficile et excitant.</p>
<p>Quand l'aide écrite — via forum ou <em>chat</em> — ne suffit pas une visioconférence à
deux avec partage d'écran de l'étudiant résout souvent le problème. C'est une
aide individuelle appréciable.</p>
<p>L'exposé oral donné en visioconférence est évidemment l'exercice le plus
difficile. Je rabâche mes étudiants et étudiantes pour qu'<em>iels</em> me montrent sur
le visage s'<em>iels</em> m'entendent, me comprennent ou pas, ont besoin d'une
explication supplémentaire ou que j'aille plus vite ou plus lentement… et bien
sûr la petite led de ma <em>webcam</em> ne me donne pas cette information très utile.
J'essaie de m'y faire. En échange il est possible de visionner l'enregistrement
à la vitesse que l'on veut et le revoir autant de fois que nécessaire… quand on
veut. Un prêté pour un rendu. C'est temporaire.</p>
<p>Bien sûr, dans la vraie vie, dans tous les groupes partout et en tout temps, les
individus étant différents, certains préfèreront les cours en présentiel alors
que d'autres se satisferont des cours à distance. À mon avis il faudra envisager
un meilleur mélange à la sortie du confinement.</p>
<p>Bien sûr, dans la vraie vie, dans tous les groupes partout et en tout temps, les
individus étant différents, il y a toujours les profs<sup id="fnref2:f2"><a href="https://blog.namok.be/?post/2020/04/14/le-professeur-ne-nous-a-rien-appris#fn:f2" rel="footnote">1</a></sup> qui font le boulot et
qui s'adaptent — avec plus ou moins de réussite — et les autres qui ne le font
pas, arguant qu'ils ou elles n'ont pas les compétences, la formation… ou
simplement l'envie. Ces seconds sont sans doute moins autonomes que les premiers
et nous ne sommes jamais « logés à la même enseigne » parce que nous sommes
différents et que nos vécus et nos réalités sont différentes<sup id="fnref3:f1"><a href="https://blog.namok.be/?post/2020/04/14/le-professeur-ne-nous-a-rien-appris#fn:f1" rel="footnote">2</a></sup>. C'est
peut-être réagir à la <em>#notAllProfs</em> que de rappeler nos différences mais je ne
peux pas m'en empêcher en lisant « […] les professeurs ayant disparus de la
circulation. » là où j'aurais préféré lire « des professeurs ». Un choix de
déterminant que fait la différence.</p>
<p>Portez-vous bien pour utiliser la formule de politesse à la mode.</p>
<p><br/></p>
<p><em>Crédit photo chez Unsplash par <a href="https://blog.namok.be/?post/2020/04/14/~/home/logo-img/images/nigel-cohen-gwpx9DOKB4w-unsplash.jpg">Nigel Cohen</a>.</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f2">
<p>J'utilise sciemment l’abréviation pour ne pas devoir écrire le
professeur ou la professeure. <a href="https://blog.namok.be/?post/2020/04/14/le-professeur-ne-nous-a-rien-appris#fnref:f2" rev="footnote">↩</a> <a href="https://blog.namok.be/?post/2020/04/14/le-professeur-ne-nous-a-rien-appris#fnref2:f2" rev="footnote">↩</a></p>
</li>
<li id="fn:f1">
<p>Accord de proximité. <a href="https://blog.namok.be/?post/2020/04/14/le-professeur-ne-nous-a-rien-appris#fnref:f1" rev="footnote">↩</a> <a href="https://blog.namok.be/?post/2020/04/14/le-professeur-ne-nous-a-rien-appris#fnref2:f1" rev="footnote">↩</a> <a href="https://blog.namok.be/?post/2020/04/14/le-professeur-ne-nous-a-rien-appris#fnref3:f1" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Fora rebornurn:md5:a055fa578091e060254e0b3bc7485b2b2020-04-14T03:39:00+02:002020-04-14T02:47:36+02:00PiTCartable au dosesiinutile<p><img src="https://blog.namok.be/public/images/divers/2020/qinghill-x8MZ2MoEKLE-unsplash-cut.jpg" alt="" style="margin: 0 auto; display: block;" /></p>
<p>Jadis. Au siècle dernier ou presque, j'avais mis en place un <strong>forum</strong> à
destination des étudiants et étudiantes.<a href="http://web.archive.org/web/20060815000000*/fora.namok.be"><em>The wabyback machine</em> me dit que
c'était en 2006</a> et ça a bien duré 9 ans. Un bail.</p>
<p>Il y avait des salons pour plusieurs cours et les étudiants et étudiantes
venaient. Le forum leur permettait de poser des questions quand iels voulaient
même en dehors des cours. Pour les profs, c'était un service rendu bien sûr mais
aussi l'occasion donnée aux étudiants et étudiantes d'apprendre à formuler
correctement une question en précisant ce qui a déjà été mis en œuvre pour
trouver une réponse. Et, en plus, on se marrait bien.</p>
<p><a href="https://blog.namok.be/public/images/divers/2020/fora-2012.png" title="Screenshot fora, 2012"><img src="https://blog.namok.be/public/images/divers/2020/.fora-2012_m.png" alt="Screenshot fora, 2012" style="margin: 0 auto; display: block;" /></a></p>
<p>Puis des étudiants ont décidé, avec l'avènement de Facebook, de créer des
groupes Facebook pour « être entre eux »… et le forum est doucement mort. Bouh.</p>
<p>Avec la grande hibernation, nous voulions proposer des moyens de communications
synchrone et asynchrone. Nous utilisons un ou plusieurs forum de Moodle
— l'outil utilisé pour la plateforme de <em>elearning</em> — pour la communication
asynchrone et <em>Hangout Chat de Google</em> pour la communication synchrone.</p>
<p>C'est un peu une renaissance de forum et je trouve toujours aussi sympa de
répondre à des questions d'étudiants et étudiantes à n'importe quelle heure de
la nuit et de la journée.</p>
<p><em>Crédit photo chez Unsplash par <a href="https://unsplash.com/photos/x8MZ2MoEKLE">Qinquil</a></em></p>
JDK10, 11 et 12, quelques nouveautésurn:md5:05efa8739e8efe40e2b9c3b5bd416a732019-04-06T18:10:00+02:002021-09-24T10:06:04+02:00PiTCartable au dosenseignementesigeekjava<p><img src="https://blog.namok.be/public/images/divers/2019/20190103_111905.jpg" alt="20190103_111905.jpg" style="margin: 0 auto; display: block;" title="20190103_111905.jpg, avr. 2019" /></p>
<p>Depuis JDK9, Oracle change le cycle de sorties des différentes versions de Java. Alors qu'avant, une nouvelle version de Java sortait tous les 2, 3 ans, c'est maintenant tous les 6 mois qu'une nouvelle version sera disponible.</p>
<p>Parmi ces version, certaines sont étiquetées <em>long term support</em> (LTS) et sont supportées par Oracle plusieurs années tandis que, pour les autres, le support ne durera que 6 mois. Les « prochaines » versions LTS de Java sont la <strong>8</strong> et la <strong>11</strong>.</p>
<p>Ce billet recense quelques nouveautés des versions 10, 11 et 12 de Java et ne sera pas exhaustif. Je ne m'intéresse qu'à quelques fonctionnalités visibles pour les développeurs et les développeuses. Mes sources principales sont les pages Oracle recensant les <em>JDK Enhanced Proposal</em> (JEP).</p>
<h1>JDK10</h1>
<p>Cette version du JDK voit passer plusieurs améliorations du <em>garbage collecor</em> (GC), par exemple du parallélisme <a href="http://openjdk.java.net/jeps/307">JEP307</a>, une amélioration du <em>class data sharing</em> (CDS) introduit avec JDK5 <a href="http://openjdk.java.net/jeps/301">JEP301</a>,la possibilité d'allouer de la mémoire sur d'autres périphériques <a href="http://openjdk.java.net/jeps/316">JEP316</a>, des améliorations internes…</p>
<p>Ce qui est le plus visible dans cette version 10, est l'inférence de type.</p>
<h2>Inférence du type des variables locales</h2>
<p><em>Local-variable type inference</em> <a href="http://openjdk.java.net/jeps/286">JEP286</a> étend l'inférence de type aux
déclarations de variables locales avec initialiseurs, la variable d'un <em>enhanced
for</em> et les variables d'un <em>for</em>. Le mot utilisé est <strong><em>var</em></strong>. Il n'est pas un
mot-clé (<em>keyword</em>) mais un nom de type réservé ce qui limitera l'impact de
l'ajout sur les codes existants.</p>
<p>Il est donc possible d'écrire :</p>
<pre><code class="java">
var i = 5;
var s = "Hello world";
var l = new ArrayList<String>();
var heu = videos.stream()
.map(v -> v.getAuthor())
.sorted()
.distinct()
.count();
var j = foo(i);
</code></pre>
<p>Le troisième exemple me convainc peu par-ce que je préfère que <code>l</code> soit de type
<code>List<String></code> et pas <code>ArrayList<String></code> mais j'aime bien le quatrième exemple
où l'on peut envisager que lorsque l'on commence à enchainer les appels de
méthodes, on ne sache pas encore très bien quel sera le type « final ».</p>
<p>Il est à noter que cela ne fonctionnera pas pour un tableau ou <code>null</code>.</p>
<p>Certaines personnes ajoutent que <code>var</code> amène une meilleur lisibilité de l'indentation (puisque le « type » fait toujours 3 lettres).</p>
<p>Pour plus de détails, lire par exemple l'article de Nicolai Parlog chez <a href="https://blog.codefx.org/java/java-10-var-type-inference/">codefx</a>. Lire également les <a href="http://openjdk.java.net/projects/amber/LVTIstyle.html">règles de bonnes pratiques</a>.</p>
<h1>JDK11</h1>
<p>La version 11 apporte — à mon sens — deux changements visibles pour les
développeurs <em>lambdas</em>.</p>
<h2>Local-variable syntax for lambda parameters</h2>
<p><em>local-variable syntax for lambda parameters</em> ajoute le mot clé <code>var</code> aux
lambdas. Là où l'on écrivait :</p>
<pre><code class="java">
(x, y) -> x.process(y)
</code></pre>
<p>on pourra écrire :</p>
<pre><code class="java">
(var x, var y) -> x.process(y)
</code></pre>
<p>Cet ajout est principalement là dans un soucis d'uniformisation de la syntaxe <a href="https://openjdk.java.net/jeps/323">JEP323</a></p>
<h2>Launch single-file source-code programs</h2>
<p>Ça y est, Java n'est plus compilé et on peut écrire des scripts Java ! Carrément des <strong><em>shebang files</em></strong> <a href="https://openjdk.java.net/jeps/330">JEP330</a>. En fait, ce n'est pas vrai
mais c'est proche.</p>
<p>Pour rappel, le lanceur Java peut lancer un fichier <code>class</code>, lancer la classe principale d'un
fichier <code>jar</code> ou encore lancer la classe principale d'un module. Aujourd'hui,
il peut également lancer une classe déclarée dans un fichier source.</p>
<p>On peut donc écrire :</p>
<pre><code class="java">
public class Hello {
public static void main (String[] args) {
System.out.println("Hello " + args[0]);
}
}
</code></pre>
<p>et exécuter le programme avec la commande suivante. Aucun fichier <code>.class</code> n'est
généré, le <em>bytecode</em> se trouve en mémoire.</p>
<pre><code class="langage-bash">
$ java Hello.java Alice
</code></pre>
<p>Si mon fichier ne respecte pas la convention de nommage java et s'appelle par
exemple <code>hello</code>, je peux écrire :</p>
<pre><code class="langage-bash">
$ java --source 11 hello Alice
</code></pre>
<p>Il reste un pas à franchir pour écrire un script en Java. Allons-y. J'écris le code suivant dans un fichier nommé <code>hello</code> et je rends le fichier exécutable.</p>
<pre><code class="java">
#!/usr/lib/jvm/java-11-openjdk-amd64/bin/java --source 11
public class Hello {
public static void main (String[] args) {
System.out.println("Hello " + args[0]);
}
}
</code></pre>
<p>Je peux alors entrer la commande :</p>
<pre><code class="bash">
./hello Alice
</code></pre>
<p>Dans les changements peu visibles pour les développeurs et développeuses, je
note :</p>
<ul>
<li><p><em>nest-based access control</em> <a href="https://openjdk.java.net/jeps/181">JEP181</a> concernant les classes internes.</p>
<p>Pour une classe <code>Outer</code> et une classe interne <code>Inner</code>, les <em>bytecodes</em>
générés dans <code>Outer.class</code> et <code>Outer$Inner.class</code> seront différents par
rapport aux <em>bytecodes</em> générés dans les versions antérieures de Java et le
contrôle d'accès au code amélioré.</p>
<p>Pour comprendre : la <a href="https://openjdk.java.net/jeps/181">JEP181</a>, un article de
Ganesh Pagade <a href="https://www.baeldung.com/java-nest-based-access-control">chez Baeldung</a> et de Peter Verhas chez
<a href="https://dzone.com/articles/nesting-java-classes">dzone</a>.</p></li>
<li><p><em>dynamic class-file constants</em> <a href="https://openjdk.java.net/jeps/309">JEP309</a> est complexe. Le but est d'étendre
le <em>constant pool</em> et d'y ajouter d'autres types de valeurs.</p>
<p>Actuellement, Java utilise une table contenant les constantes du programme,
le (<em>constant pool</em>). C'est assez facile à comprendre pour les types
primitifs et les chaines (<code>String</code>). C'est d'ailleurs pour ça que :</p>
<p></p>
<pre><code class="java">
String s1 = "Marlène";
String s2 = "Marlène";
System.out.println(s1 == s2); // true
</code></pre>
<p>Au fil des versions de Java, d'autres valeurs ont été ajoutées à ce
<em>constant pool</em> comme les « littéraux de classes constants » (<em>class literal
constants</em>). Toutes les classes, les interfaces, les énumérations… sont des
instances de <code>Class</code> dans une application Java et <code>Class</code> permet par exemple
l'introspection :</p>
<pre><code class="java">
Class<String> c = String.class;
System.out.println(Arrays.toString(c.getMethods()));
</code></pre>
<p><code>String.class</code> est une instance de <code>Class<String></code> comme <code>"Hello world"</code> est
une instance de <code>String</code>. <code>String.class</code> est constant et se trouve dans le
<em>constant pool</em>.</p>
<p>Actuellement si <code>A</code> et <code>B</code> sont deux constantes et se trouvrent donc dans
le <em>constant pool</em>, <code>Math.max(A, B)</code> ne s'y trouve pas. Ceci devrait
évoluer.</p></li>
<li><p><em>HTTP client</em> standardisé comme présenté dans le JDK9. J'en avais parlé dans
<a href="http://namok.be/blog/?post/2018/03/22/JDK9#http2">ce billet</a>.</p></li>
<li><p>mise à jour pour rester <em>up to date</em> : <em>Key Agreement with Curve25519 and
Curve448</em>, <em>ChaCha20 and Poly1305 cryptographic algorithms</em>, <em>Unicode 10</em> et
<em>TLS1.3</em></p></li>
</ul>
<p>Pour le reste dans les changements apportés au JDK et non présentés ici, ça se
passe <a href="https://openjdk.java.net/projects/jdk/11/">là JDK11</a></p>
<h1>JDK12</h1>
<p>Dans cette version du JDK, une seule fonctionnalité est destinée aux
développeurs et développeuses « standard ». Il s'agit du <strong><em>switch</em></strong> qui
devient une expression (<a href="https://openjdk.java.net/jeps/325">JEP325</a>). <em>switch</em>
pourra être utilisé comme une <strong>instruction</strong> — comme avant — ou comme une
<strong>expression</strong>.</p>
<p><a href="https://openjdk.java.net/jeps/12">Cette fonctionnalité est en <em>preview</em></a>
c'est-à-dire qu'elle est pleinement fonctionnelle et documentée et est présente
afin d'être complètement testée. Elle pourrait disparaitre et pour être utilisée
il est nécessaire de compiler et d'exécuter son code avec le <em>switch</em> <code>--enable-preview</code>.</p>
<ol>
<li><p>Au <code>case <label>:</code> s'ajoute un <code>case <label> -></code></p>
<p>Ceci permet de ne pas utiliser de <code>break</code> car seule l'instruction (ou le
bloc d'instructions) suivant la flèche sera exécuté.
On pourra donc écrire, pour peu qu'une <code>enum</code> <em>Season</em> existe :</p>
<pre><code class="java">
Season s = // a season
switch (s) {
case SPRING, SUMMER -> System.out.println("It's hot");
case WINTER, AUTUMN -> System.out.println("It's cold");
default -> System.out.println("Claim climate change");
}
</code></pre></li>
<li><p>Le <em>switch</em> est désormais également une <strong>expression</strong>, il a un type et une
valeur. On pourra par exemple écrire :</p>
<pre><code class="java">
Season s = // a season
String message = switch(s){
case SPRING, SUMMER -> "It's hot";
case WINTER, AUTUMN -> "It's cold";
};
</code></pre></li>
</ol>
<p>Les autres fonctionnalités concernent la <em>java virtual machine</em> (JVM). Le
<em>garbage collecor</em> (GC) : ajout de <em>Shenandoah</em>, un GC à faible temps de pause
à titre expérimental et, à ma connaissance, c'est toujours G1 qui est utilisé.
Optimisation de G1 pour qu'il rende plus rapidement la mémoire non utilisée du
tas (<em>heap</em>) au système lorsque le processus Java est en pause. Pratique pour
les environnements conteneurisés et possibilité de fixer un objectif de temps
maximal d'exécution du <em>garbage collector</em>. Au sujet des architectures, <em>One
AArch64 Port, Not Two</em>, supprime un des deux ports de Java pour les
architectures ARM pour n'en conserver qu'une seule.</p>
<p>Pour les détails, ça se passe <a href="https://openjdk.java.net/projects/jdk/12/">là JDK12</a></p>
<p>Au vu des cycles de développement, je pense que je vais avoir tendance à utiliser les versions LTS.</p>
<p><br/></p>
<p><em>Crédit photo personnel. Petit souvenir de quelques jours à la mer…</em></p>
Debian GNU/Linux sur le Microsoft Storeurn:md5:655df9ced7fe2e2ba04bb8748c1992852018-10-05T08:56:00+02:002018-10-05T07:58:14+02:00PiTMes doigts dans le clavierdebianesigeekjavapratique<p><img src="https://blog.namok.be/public/images/divers/2018/debian-app-4.jpg" alt="debian-app-4.jpg" style="margin: 0 auto; display: block;" title="debian-app-4.jpg, oct. 2018" /></p>
<p><strong>Debian GNU/Linux est disponible sur le Microsoft Store.</strong></p>
<p>Pour apprendre les bases linux sans effort d'installation — fini le <em>dual boot</em>
et les machines virtuelles — il est possible d'utiliser l'application Debian
Linux — ou une autre distribution — qui se trouve sur le Microsoft Store !</p>
<p><img src="https://blog.namok.be/public/images/divers/2018/.debian-app_m.jpg" alt="debian-app.jpg" style="margin: 0 auto; display: block;" title="debian-app.jpg, oct. 2018" /></p>
<p>J'ai eu l'occasion de mettre les mains sur une machine Microsoft Windows 10 et
j'ai donc pu constater l'existence de <em>Microsoft Store</em>. C'est une bonne chose.
J'ai ensuite installé « l'application Debian GNU/Linux ». Notez la présence de
« GNU » qui fera plaisir à <em>rms</em>.</p>
<p>Une fois installée, Microsoft signale qu'il faut activer le <strong>WSL</strong> <em>Windows
Subsystem for Linux</em> ou encore <em>Sous-système Windows pour Linux</em>. Ça se fait en
deux clics et un reboot… parce que bon, un <em>reboot</em> sous Windows, c'est toujours
bien.</p>
<p><img src="https://blog.namok.be/public/images/divers/2018/debian-app-23.jpg" alt="debian-app-23.jpg" style="margin: 0 auto; display: block;" title="debian-app-23.jpg, oct. 2018" /></p>
<p>Un fois le reboot fait, on peut profiter d'une console Debian sous Windows. Si
vous cherchez un peu, vous verrez que sans installation et avec simplement <em>WSL</em>
activé, vous avez accès à <em>bash</em>. Vous verrez aussi qu'il existe d'autres
distribution proposées par Microsoft.</p>
<p><a href="https://blog.namok.be/public/images/divers/2018/debian-app-4.jpg" title="debian-app-4.jpg"><img src="https://blog.namok.be/public/images/divers/2018/.debian-app-4_m.jpg" alt="debian-app-4.jpg" style="margin: 0 auto; display: block;" title="debian-app-4.jpg, oct. 2018" /></a></p>
<p>Windows propose une <em>console linux</em> et pas une distribution complète. Pas
d'environnement de bureau par exemple. L'usage sera donc <em>textuel</em><sup id="fnref:f1"><a href="https://blog.namok.be/?post/2018/10/05/debian-microsoft#fn:f1" rel="footnote">1</a></sup>.</p>
<p>Je salue l'initiative de Microsoft tout en me disant que cette console Linux
dans l'environnement Windows, bien que très pratique, ne va pas faciliter la
compréhension des concepts « systèmes » dans les têtes des débutants et
débutantes. Nous avons donc bien affaire à la possibilité d'inclure un sous
système Linux dans un système Microsoft Windows. Même s'il ne s'agit pas d'une
machine virtuelle bien emballée, ce n'est pas un système Linux natif non plus.</p>
<p>— Qu'est-ce que WSL ?</p>
<p>WSL, <em>Windows Subsystem Linux</em> est un ensemble de modules permettant d'exécuter
des applications Linux <em>elf64</em> sous Microsoft Windows. <a href="https://blogs.msdn.microsoft.com/wsl/2016/04/22/windows-subsystem-for-linux-overview/">Voir la présentation
générale (en) sur le blog msdn</a>.</p>
<p>La notion de sous-système n'est pas neuve puisque Win32, par exemple, est un
sous-système de Microsoft Windows NT (NT dans la suite) qui présente l'interface
de programmation aux applications. Ce sous-système exécute les appels systèmes
NT appropriés en fonction de l'API qu'il présente.</p>
<p>Les binaires Linux sont lancés dans des processus particuliers — les <em>processus
pico</em> — au lieu des <em>processus NT</em>. Au contraire des processus NT qui embarquent
le sous-système Win32, les processus pico embarquent les pilotes <em>lxss.sys</em>
et <em>lxcore.sys</em> qui se chargent de la traduction des appels systèmes Linux en
appels systèmes NT et de l'émulation du noyau Linux. <a href="https://blogs.msdn.microsoft.com/wsl/2016/05/23/pico-process-overview/">Plus d'info sur les
processus pico (en) sur le blog de msdn</a> et <a href="https://blogs.msdn.microsoft.com/wsl/2016/06/08/wsl-system-calls/">sur l'émulation du noyau
Linux (en) sur le même blog</a>.</p>
<p>Chaque processus lancé à partir de WSL — via bash.exe ou l'application Debian
— est un processus apparaissant dans la liste des processus Windows. C'est un
<em>processus pico</em> et non un <em>processus NT</em>.</p>
<p><a href="https://docs.microsoft.com/en-us/windows/wsl/faq">Pour les questions, la FAQ Microsoft est dispo</a>.</p>
<p><em>Crédit photo faite maison.</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Il est toujours possible d'installer un serveur X sur Windows et
d'exporter son <em>display</em> « à l'ancienne » mais ce n'est pas offciellement
supporté. <a href="https://blog.namok.be/?post/2018/10/05/debian-microsoft#fnref:f1" rev="footnote">↩</a></p>
</li>
</ol>
</div>
JDK9, les nouveautésurn:md5:1c05ae39e1f7e69af1622d5ad0b5664f2018-03-22T17:02:00+01:002018-05-07T15:28:57+02:00PiTMes doigts dans le clavierenseignementesigeekjava<p><img src="https://blog.namok.be/public/images/divers/2018/unzipped_egg_by_pixelduster_cut.jpg" alt="unzipped_egg_by_pixelduster_cut.jpg" style="margin: 0 auto; display: block;" title="unzipped_egg_by_pixelduster_cut.jpg, mar. 2018" /></p>
<p><a href="https://blog.namok.be/?post/2018/03/22/JDK9#installation">Préalables pratiques</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#of">List, Set, Map of</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#modules">Les modules, <em>Java Platform Module System (JPMS)</em> ou encore <em>Jigsaw</em></a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#processus">Gestion des processus systèmes</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#jshell">Jshell</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#stream">Nouvelles méthodes pour la classe Stream</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#javadoc">Javadoc</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#underscore">_ underscore est un mot clé</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#optional">La classe <em>Optional</em> se dote de nouvelles méthodes</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#version">Numérotation des versions</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#sha">Algorithmes de hashages supplémentaires</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#string">Réduction de l'espace de stockage des chaines</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#unicode">Support d'unicode 8.0</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#http2">http2</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#inputstream">La classe <em>InputStream</em> se dote également de nouvelles méthodes</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#flow">La classe <code>Flow</code>, le producteur et le consommateur</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#more">Quelques points non détaillés</a></p>
<hr />
<p>Alors que <em>JDK 10</em> est sorti le 20 mars, je vous parle de <em>JDK 9</em>.
Chaque chose en son temps…</p>
<h2 id="installation">Préalables pratiques</h2>
<p>Pour tester JDK9, j'utilise <strong>Netbeans</strong>. Netbeans 8.1 et Netbeans 8.2 ne
permettent pas d'utiliser JDK9. Il est nécessaire d'installer une version de
développement <a href="http://wiki.netbeans.org/JDK9Support">comme indiqué dans le wiki</a> et <a href="https://stackoverflow.com/questions/42734944/netbeans-8-2-with-jdk9-build160">sur Stackoverflow</a>
pour celles et ceux qui préfèrent.</p>
<p>Il est nécessaire d'installer un JDK9. Sans blague. J'installe <em>jdk-9.0.4</em>.</p>
<p>Dans la suite, je supposerai que mon installation se trouve dans
<strong><code>/usr/lib/jvm/jdk9</code></strong>. Je regarde l'arborescence — via la commande <code>tree</code>
— qui a l'allure suivante :</p>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-1.png" title="jdk9-1.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-1_m.png" alt="jdk9-1.png" style="margin: 0 auto; display: block;" title="jdk9-1.png, mar. 2018" /></a></p>
<p>On constate d'emblée que l'organisation des fichiers a changé… ce qui est le
corollaire immédiat de la notion de modules — voir ci-dessous — introduite dans
le JDK.</p>
<p>Je repère aussi le fichier <code>src.zip</code> que je décompresse aussitôt et que je
conserve au chaud pour plus tard. Son organisation est semblable
à l'organisation en module du répertoire <em>jmods</em>.</p>
<p>J'essaierai de citer les JEP, <em>JDK Enhancement Proposals</em> en rapport avec les
différents points.</p>
<p>Dans certains exemples, je suppose l'existence d'une classe <code>Video</code> représentant
une vidéo. Une vidéo a un auteur, un titre, un nombre de <em>likes</em> et un état
publiée ou pas. Je suppose également, l'existence d'une <em>factory</em> associées:
<code>Videos</code>.</p>
<h2 id="of">List, Set, Map of</h2>
<p>Les interfaces <code>List</code>, <code>Set</code> et <code>Map</code> reçoivent des méthodes <code>of</code> (<a href="http://openjdk.java.net/jeps/269">JEP269</a>).
Ces méthodes retournent une <code>List</code>, un <code>Set</code> ou une <code>Map</code> <strong>immuables</strong>
contenant les objets passés en paramètres. Elles sont bien sûr génériques
(<em>generics</em>). Je peux par exemple écrire pour un <code>Set</code>:</p>
<pre><code class="language-java">
Set<String> s = Set.of("Vicky", "Jenny", "Karine");
</code></pre>
<p>alors que je devais écrire:</p>
<pre><code class="language-java">
Set<String> oldWay = new HashSet(
Arrays.asList("Vicky", "Jenny", "Karine"));
oldWay = Collections.unmodifiableSet(oldWay);
</code></pre>
<p>Pour une liste que je voudrais modifiable, je peux écrire;</p>
<pre><code class="language-java">
List<Integer> l = new ArrayList<>(
List.of(2,3,-5));
</code></pre>
<p>Je ne résiste pas à la tentation de vérifier<sup id="fnref:f1"><a href="https://blog.namok.be/?post/2018/03/22/JDK9#fn:f1" rel="footnote">1</a></sup> que ces méthodes statiques ont
bien été ajoutées dans l'interface <code>List</code>. Un <code>find</code> plus tard, je trouve le
fichier source que je peux éditer.</p>
<pre><code class="language-sh">
$ find . -name List.java
./src/java.desktop/java/awt/List.java
./src/java.xml.bind/com/sun/xml/internal/bind/v2/schemagen/
xmlschema/List.java
./src/jdk.compiler/com/sun/tools/javac/util/List.java
./src/java.base/java/util/List.java
$ gvim src/java.base/java/util/List.java
</code></pre>
<p>Extraits:</p>
<pre><code class="language-java">
public interface List<E> extends Collection<E> {
…
/**
* Returns an immutable list containing one element.
*
* See <a href="https://blog.namok.be/?post/2018/03/22/JDK9#immutable">Immutable List Static Factory
* Methods</a> for details.
*
* @param <E> the {@code List}'s element type
* @param e1 the single element
* @return a {@code List} containing the specified element
* @throws NullPointerException if the element is {@code null}
*
* @since 9
*/
static <E> List<E> of(E e1) {
return new ImmutableCollections.List1<>(e1);
}
</code></pre>
<h2 id="modules">Les modules, <em>Java Platform Module System (JPMS)</em> ou encore <em>Jigsaw</em></h2>
<p>Comme on l'a vu, Java découpe son code en modules — <em>Jigsaw</em> de petit nom
— dépendants les uns des autres. Cette découpe en modules — outre qu'elle
réorganise l'arborescence des sources — permet de structurer le JDK d'une part
et d'autoriser uniquement le chargement des modules nécessaires d'autre part.
Cette restructuration du <em>jdk</em> et du <em>jre</em> est sensée offrir de meilleures
performances, plus de sécurité et une maintenance plus aisée.<br />
Elle est décrite en partie dans <a href="http://openjdk.java.net/jeps/201">JEP201</a>, <a href="http://openjdk.java.net/jeps/261">JEP261</a> et <a href="http://openjdk.java.net/jeps/200">JEP200</a>.</p>
<p>Les fichiers java — et particulièrement le <em>bytecode</em> — sont réorganisées.
Toutes les classes standards ne se trouvent plus dans <code>rt.jar</code> et <code>tools.jar</code>…
qui ont disparus <a href="http://openjdk.java.net/jeps/220">JEP220</a>. Les classes se trouvent dans le répertoire <em>jmods</em>
contenant des fichiers au format <em>jmods</em>.</p>
<p>Le code se trouvant dans les modules et dans les fichiers <em>jar</em> traditionnels
basés sur le <em>classpath</em> peuvent coexister.</p>
<p>Cette notion de modules introduit:</p>
<ul>
<li><p>une sorte d'édition des liens — optionnelle — entre la phase de compilation et
celle d'exécution. Cette phase assemble les modules qui seront utilisés
à l'exécution grâce à <a href="https://docs.oracle.com/javase/9/tools/jlink.htm#JSWOR-GUID-CECAC52B-CFEE-46CB-8166-F17A8E9280E9">jlink</a>.</p></li>
<li><p>la notion de fichier <em>jar</em> modulaire qui est un fichier <em>jar</em> contenant un
fichier <code>module-info.class</code> à la racine. Ce fichier définit (voir plus bas)
l'organisation du module.</p></li>
<li><p>le format <em>jmod</em>, semblable au format <em>jar</em>, mais pouvant inclure du code natif
et des fichiers de configuration. Voir <a href="https://docs.oracle.com/javase/9/tools/jmod.htm#JSWOR-GUID-0A0BDFF6-BE34-461B-86EF-AAC9A555E2AE">jmod</a>.</p></li>
</ul>
<p>Pour définir un module, il est nécessaire d'ajouter un fichier
<code>module-info.java</code> à la racine du projet contenant:</p>
<pre><code class="language-java">
module org.example.my.module {
requires net.example.module.need;
export org.example.my.module.services;
}
</code></pre>
<p><strong>module</strong> définit le module, <strong>requires</strong> précise quels sont les modules
nécessaires et <strong>export</strong> présente publiquement les <em>packages</em> qui seront donc
visibles.</p>
<p>Voir par exemple <code>src/java.base/module-info.java</code><sup id="fnref:f2"><a href="https://blog.namok.be/?post/2018/03/22/JDK9#fn:f2" rel="footnote">2</a></sup>.</p>
<p>À <code>java</code> et <code>javac</code> s'ajoutent deux commandes:</p>
<ul>
<li><code>jlink</code> assemble des modules;</li>
<li><code>jdeps</code> informe sur les dépendances.</li>
</ul>
<h3>Exemple, <em>Hello, modular world</em></h3>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-7-modular.png" title="jdk9-7-modular.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-7-modular_s.png" alt="jdk9-7-modular.png" style="float: right; margin: 0 0 1em 1em;" title="jdk9-7-modular.png, mar. 2018" /></a></p>
<p>J'ai essayé d'utiliser Netbeans pour créer un module
avec un <em>new Java Modular Project</em>,
mais ça n'a pas été concluant.</p>
<p>Netbeans me crée bien un projet
auquel je peux ajouter un module. Il me crée alors un fichier <code>module-info.java</code>
que je peux compléter. Je crée un <em>package</em>, j'y place une classe et je clique
sur <em>"Run project…"</em>. Netbeans me demande de choisir une classe principale et
tout roule… sauf si je veux lancer le projet en dehors de Netbeans. La commande
qu'il me propose — et d'autres variantes — ne fonctionne pas.</p>
<p>Reprenons depuis le début en créant un projet tout à fait standard cette fois.</p>
<ol>
<li>Création d'un projet </li>
<li><p>Ajout d'un <em>package</em> contenant une classe <code>Hello</code> et ce code:</p>
<pre><code class="language-java">
package be.example.hello;
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, modular world");
}
}
</code></pre></li>
<li><p>Clic "Run" et ajout de la classe comme classe principale.</p></li>
<li><p>Ajout d'un fichier <code>module-info</code> via Netbeans. Il le crée en choisissant le
nom du module en fonction du nom du projet:</p>
<pre><code class="language-java">
module Jdk9Modular {
}
</code></pre></li>
<li><p>Ajout de la ligne</p>
<pre><code class="language-java">
exports be.example.hello;
</code></pre></li>
<li><p><em>clean and build</em> et Netbeans me propose une commande à exécuter.
Tout roule…</p>
<pre><code class="language-sh">
java
-p /elsewhere/jdk9-modular/dist/jdk9-modular.jar
-m Jdk9Modular
</code></pre></li>
</ol>
<p>Et si je voulais entrer les commandes « à la main » dans mon terminal.
Je me <a href="https://blog.codefx.org/java/java-module-system-tutorial/">base sur cette documentation</a> que j'adapte à mon projet Netbeans
et je suppose que je me trouve à la racine de mon projet.</p>
<pre><code class="language-sh">
javac -d build/classes
src/be.example.hello/classes/module-info.java
src/be.example.hello/classes/be/example/hello/Hello.java
jar --create
--file build/hello-modular-world.jar
--main-class be.example.hello.Hello
-C build/classes .
java
--module-path build/hello-modular-world.jar
--module be.example.hello
java
-p build/hello-modular-world.jar
-m be.example.hello
</code></pre>
<p>Les deux dernières commandes sont équivalentes. Notons qu'il n'est plus
nécessaire d'écrire un <em>manifest</em>, il suffit de renseigner la classe principale
avec <code>--main-class</code>.</p>
<h2 id="processus">Gestion des processus systèmes</h2>
<p>Une API dédiée à la gestion des processus et décrite dans <a href="http://openjdk.java.net/jeps/102">JEP102</a>. Avant
JDK9, il était possible de lancer des processus mais pas de les contrôler
ensuite.</p>
<p>Par exemple:</p>
<pre><code class="language-java">
Runtime.getRuntime().exec("/usr/bin/xeyes");
ProcessBuilder pb = new ProcessBuilder("/usr/bin/xeyes");
pb.start();
</code></pre>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-2-processhandle.png" title="jdk9-2-processhandle.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-2-processhandle_s.png" alt="jdk9-2-processhandle.png" style="float: right; margin: 0 0 1em 1em;" title="jdk9-2-processhandle.png, mar. 2018" /></a></p>
<p>La classe <code>ProcessHandle</code> offre moult méthodes pour contrôler un processus.
Ces méthodes lancent des <code>RuntimeException</code> ce qui est plus dans le mouvement
actuel par rapport aux exceptions contrôlées.</p>
<p>Je peux accéder au processus en cours mais également aux parents et aux enfants.
Si l'on compare à <code>ProcessBuilder</code> c'est beaucoup plus complet.</p>
<p>Puisque l'on a accès à une méthode <code>destroy</code> et à notre <code>pid</code>, essayons de nous
suicider. Je sais, c'est triste… et Java nous en empêche. Tout va bien ;-)</p>
<pre><code class="language-java">
ProcessHandle me = ProcessHandle.current();
System.out.printf("My process id: %d\n", me.pid());
System.out.printf("I'll try to kill myself (so sad)");
me.destroy();
</code></pre>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-3-killmyself.png" title="jdk9-3-killmyself.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-3-killmyself_m.png" alt="jdk9-3-killmyself.png" style="margin: 0 auto; display: block;" title="jdk9-3-killmyself.png, mar. 2018" /></a></p>
<h2 id="jshell">Jshell</h2>
<p><em>jShell</em> est une boucle REPL (<em>Read, Evaluate, Print, Loop</em>) proposant une sorte
de <em>shell</em> en Java (un peu à l'instar de Python). <em>jShell</em> propose une série de
commandes et une autocomplétion avec la touche [TAB]. Par exemple: <code>list</code> liste
les instructions entrées depuis le début de la session, <code>vars</code> liste les
variables déclarées, <code>history</code> pour l'historique, etc.</p>
<p>Extrait de l'aide:</p>
<pre><code>jshell> /help
| Type a Java language expression, statement, or declaration.
| Or type one of the following commands:
| /list [<name or id>|-all|-start]
| list the source you have typed
| /edit <name or id>
| edit a source entry referenced by name or id
| … <cut>
</code></pre>
<p>Pour quitter, c'est <em>/exit</em>. Voir ci-dessous pour un exemple de « séance
jShell ».</p>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-4-jshell.png" title="jdk9-4-jshell.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-4-jshell_m.png" alt="jdk9-4-jshell.png" style="margin: 0 auto; display: block;" title="jdk9-4-jshell.png, mar. 2018" /></a></p>
<p>Pour voir le contenu d'une variable, inutile d'écrire <code>System.out.print</code>, son
nom suffit:</p>
<pre><code class="language-jshell">
jshell> double var = 3.14;
var ==> 3.14
jshell> System.out.print
print( printf( println(
jshell> System.out.println(var);
3.14
jshell> var
var ==> 3.14
</code></pre>
<p>Pour utiliser une classe d'un <em>package</em> spécifique, il y a 3 manières de faire —
merci <a href="https://stackoverflow.com/questions/43111018/how-to-import-external-libraries-in-jshell-java-9/43112866">stacxoverflow</a>:</p>
<ol>
<li>mettre à jour son <em>CLASSPATH</em> avant de lancer <em>JShell</em>;</li>
<li><p>utiliser l'option idoine de la commande <em>jshell</em>;</p>
<pre><code class="language-jshell">
jshell --class-path beautifuljar.jar
</code></pre></li>
<li><p>utiliser la commande interne <em>/env</em></p>
<pre><code class="language-jshell">
/env -class-path beautifuljar.jar
</code></pre></li>
</ol>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-8-jshell-import.png" title="jdk9-8-jshell-import.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-8-jshell-import_m.png" alt="jdk9-8-jshell-import.png" style="margin: 0 auto; display: block;" title="jdk9-8-jshell-import.png, mai 2018" /></a></p>
<h2 id="stream">Nouvelles méthodes pour la classe Stream</h2>
<p>Quatre nouvelles méthodes pour <code>Stream</code>: <code>takeWhile</code>, <code>dropWhile</code>,
<code>ofNullable</code> et <code>iterate</code>.</p>
<p><em>takeWhile</em> — et c'est pareil pour <em>dropWhile</em> — est une méthode qui va
<strong>prendre</strong> les éléments <strong>tant que</strong> la condition est respectée et c'est en ça
qu'elle diffère de <em>filter</em> qui parcourt tout le flux.</p>
<p>Attention, si le flux n'est pas ordonné, <code>take|dropWhile</code>retourne n'importe quel
sous-ensemble correspondant à la condition… même si l'on peut supposer que le
développeur s'arrêtera dès qu'il aura trouvé un faux et ne retournera pas un
autre sous-ensemble du flux.</p>
<blockquote>
<p>If this stream is unordered, and some (but not all) elements of this stream
match the given predicate, then the behavior of this operation is
nondeterministic; it is free to take any subset of matching elements (which
includes the empty set).</p>
<p>Extrait de la Javadoc</p>
</blockquote>
<p>Par exemple:</p>
<pre><code class="language-java">
System.out.print("\nFilter ");
Stream.of(1,2,3,1,2,3,1,2,3)
.filter(i -> i<3)
.forEach(i -> System.out.printf("%d ", i)); // Filter 1 2 1 2 1 2
System.out.print("\nTake while ");
Stream.of(1,2,3,1,2,3,1,2,3)
.takeWhile(i -> i<3)
.forEach(i -> System.out.printf("%d ", i)); // Take while 1 2
</code></pre>
<p><em>iterate</em> voit apparaitre une nouvelle version avec un argument supplémentaire.
Là ou l'on utilisait <em>limit</em> comme par exemple:</p>
<pre><code class="language-java">
Stream.iterate(1, n -> n+1)
.limit(9)
.forEach(i -> System.out.printf("%d ", i));
</code></pre>
<p>on pourra directement mettre un <em>predicate</em> — bien plus général donc que <em>limit</em>
— en argument. Un peu comme:</p>
<pre><code class="language-java">
Stream.iterate(1, n -> n<10, n -> n+1)
.forEach(i -> System.out.printf("%d ", i));
</code></pre>
<p><em>ofNullable</em> retourne un <em>stream</em> d'un élément ou un <em>stream</em> vide dans le cas
d'un élément <em>null</em>. Ouais.</p>
<h2 id="javadoc">Javadoc</h2>
<p>Remise en forme légère au niveau graphique de la <em>javadoc</em>, passage à HTML5,
support des commentaires <em>javadoc</em> dans les déclarations de modules et
<strong>ajout d'une zone de recherche</strong>. Et ça s'est bien.</p>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-5-javadoc.png" title="jdk9-5-javadoc.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-5-javadoc_m.png" alt="jdk9-5-javadoc.png" style="margin: 0 auto; display: block;" title="jdk9-5-javadoc.png, mar. 2018" /></a></p>
<h2 id="underscore">_ underscore est un mot clé</h2>
<p>Le caractère <code>_</code> est devenu un <em>keyword</em> en java. Il ne peut plus être utilisé
comme un <em>identifier</em>.</p>
<h2 id="optional">La classe <em>Optional</em> se dote de nouvelles méthodes</h2>
<p>Quatre nouvelles méthodes également pour la classe <code>Optional</code>: <code>ifPresent</code>,
<code>ifPresentOrElse</code>, <code>or</code> et <code>stream</code>.</p>
<pre><code class="language-java">
/*
* If factory fail to give a Video, create new video.
* Silly example.
*/
Optional<Video> ov = Optional
.of(Videos.getRandomVideo())
.or(() -> Optional.of(
new Video("Beautiful Author", "My beautiful video")));
ov.ifPresent(v -> v.like());
</code></pre>
<h2 id="version">Numérotation des versions</h2>
<p>La numérotation des versions est revue — on abandonne définitivement le 1.x — et
suit le schéma suivant (<a href="http://openjdk.java.net/jeps/223">JEP223</a>):</p>
<pre><code class="language-sh">
$MAJOR.$MINOR.$SECURITY.$PATCH
</code></pre>
<h2 id="sha">Algorithmes de hashages supplémentaires</h2>
<p>Ajout des algorithmes <em>SHA3</em> (<a href="http://openjdk.java.net/jeps/283">JEP283</a>).</p>
<pre><code class="language-java">
StringJoiner sj = new StringJoiner(" ", "Algorithms: ", "\n");
Security.getAlgorithms("MessageDigest")
.forEach(s -> sj.add(s));
System.out.print(sj);
</code></pre>
<pre><code class="language-sh">
Algorithms: SHA3-512 SHA-384 SHA SHA3-384 SHA-224 SHA-512/256
SHA-256 MD2 SHA-512/224 SHA3-256 SHA-512 MD5 SHA3-224
</code></pre>
<p>Pour calculer un <em>hash</em>, on peut écrire le code suivant et vérifier que le
<em>hash</em> est le même que celui fourni par <code>echo -n "Beautifulmessage" | sha3sum
-a 512</code>:</p>
<pre><code class="language-java">
String message = "Beautifulmessage";
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA3-512");
md.update(message.getBytes());
byte[] bs = md.digest();
System.out.printf("Message: %s\n", message);
System.out.printf("Digest: ");
for (byte b : bs) {
System.out.printf("%02X", b);
}
System.out.println("");
} catch (NoSuchAlgorithmException ex) {
System.err.println("Algorithm error: " + ex.getMessage());
}
</code></pre>
<h2 id="string">Réduction de l'espace de stockage des chaines</h2>
<p>La représentation interne des chaines de caractères (<em>strings</em>) économise de
l'espace. Là où un <em>string</em> était stocké dans un tableau de <em>char</em> (2
<em>bytes</em> par caractère), il l'est maintenant dans un tableau de <em>byte</em> et un
attribut représentant l'encodage,<em>encoding-flag field</em> appelé <em>coder</em> (<a href="http://openjdk.java.net/jeps/254">JEP254</a>).</p>
<p>Les caractères constituants les chaines étaient codés en UTF-16, chaque caractère
occupant 1 <em>char</em> (parfois 2), soient 2 <em>bytes</em> (parfois 4). La plupart des
chaines de caractères ne contiennent que des caractères Latin-1 (ISO-8859-1). Un
caractère Latin-1 est codé sur 1 <em>byte</em>. La nouvelle classe <code>String</code> stocke les
caractères <strong>soit</strong> en Latin-1 (ISO-8859-1) <strong>soit</strong> en UTF-16 en fonction du
contenu de la chaine.</p>
<p>Extraits de code source de la classe <em>String</em> sans les commentaires:</p>
<pre><code class="language-java">
// JDK8
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
private int hash; // Default to 0
private static final long serialVersionUID =
-6849794470754667710L;
</code></pre>
<pre><code class="language-java">
// JDK9
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final byte[] value;
private final byte coder;
private int hash; // Default to 0
private static final long serialVersionUID =
-6849794470754667710L;
</code></pre>
<p>Java voit apparaitre deux nouvelles classes <code>StringUTF16</code> et <code>StringLatin1</code> et
la classe <code>String</code> se meuble d'instructions de style:</p>
<pre><code class="language-java">
if (isLatin1()) {
StringLatin1.foo();
} else {
StringUTF16.foo();
}
</code></pre>
<h2 id="unicode">Support d'unicode 8.0</h2>
<p>JDK8 supportait Unicode 6.2, JDK9 supporte Unicode 8.0 (<a href="http://openjdk.java.net/jeps/267">JEP267</a>). <em>That's all</em>.</p>
<h2 id="http2">http2</h2>
<p>Java passe d'une implémentation de HTTP/1.1 à HTTP/2 (<a href="http://openjdk.java.net/jeps/110">JEP110</a>). HTTP/1.1
posait quelques problèmes pour un <em>web</em> du XXI<sup>e</sup> siècle</p>
<ul>
<li><p>fins de lignes bloquants (<em>head of lines blocking</em> )</p>
<p>Les réponses du serveur sont reçues dans le même ordre qu'elles ont été
envoyées. Avec HTTP/2, les réponses peuvent être multiplexées. S'il faut
charger une grande page html contenant des images, il ne faudra plus
attendre le chargement complet de la page avant le chargement des images.</p></li>
<li><p>nombre de connexions réduit;</p>
<p><em>A single-user client SHOULD NOT maintain more than 2 connections with any
server or proxy. (<a href="https://tools.ietf.org/html/rfc2616">RFC2616</a>)</em></p></li>
<li><p>les <em>headers</em> sont toujours envoyés en texte. Avec HTTP/2, certains <em>headers</em>
seront envoyés en binaire plutôt qu'en texte quand l'efficacité le demande;</p></li>
</ul>
<p>HTTP/2 nous promet (voir <a href="https://www.mnot.net/blog/2014/01/30/http2_expectations">cette note de blog (en)</a>); la même API (<em>same
API</em>), des requêtes moins couteuses (<em>cheaper requests</em>), un réseau plus
convivial limitant les connexions (<em>network and server friendliness</em>), <em>cache
pushing</em> (le serveur est capable de fournir des données sans requêtes du
client), avec les connexions persistantes, le développeur pourra concevoir son
application différemment (<em>change your mind</em>) et plus de chiffrement (<em>more
encryption</em>).</p>
<p>L'API fournit trois classes; <code>HTTPClient</code>, <code>HTTPRequest</code> et <code>HTTPResponse</code>. Ces
classes permettent d'instancier un client, de formuler une requête et d'attendre
une réponse. Facile. Faire une requête d'une page se fait avec un code
à l'allure suivante (<a href="https://labs.consol.de/development/2017/03/14/getting-started-with-java9-httpclient.html">source</a>):</p>
<pre><code class="language-java">
try {
HttpClient client = HttpClient
.newBuilder()
.version(HttpClient.Version.HTTP_2)
.build();
HttpRequest request = HttpRequest
.newBuilder(new URI("http://pit.namok.be"))
.GET()
.build();
HttpResponse<String> response = client
.send(request, HttpResponse.BodyHandler.asString());
System.out.println(response.statusCode());
System.out.println(response.body());
} catch (<some exceptions> ex) {
ex.printStackTrace();
}
</code></pre>
<p>Les classes <code>HttpClient</code> et <code>HttpRequest</code> ont une méthode <code>newBuilder</code> acceptant
toute une série de paramètres (version…) qu'il suffit de chainer avant d'appeler
la méthode <code>build</code> qui construira l'objet.</p>
<p>Faire une requête <em>https</em> est aussi simple que l'ajout d'un <strong>s</strong> dans l'url.</p>
<p>Pour stocker le résultat de la requête dans un fichier il faudra le signaler via
le paramètre <em>BodyHandler</em>. <code>BodyHandler.asFile(…)</code>. Par exemple:</p>
<pre><code class="language-java">
try {
HttpClient client = HttpClient
.newBuilder()
.version(HttpClient.Version.HTTP_2)
.build();
HttpRequest request = HttpRequest
.newBuilder(new URI("https://pit.namok.be"))
.GET()
.build();
Path tempFile = Files.createTempFile("http2-test", ".html");
HttpResponse<Path> response = client
.send(request,
HttpResponse.BodyHandler.asFile(tempFile));
System.out.println(response.statusCode() + "\n"
+ response.body());
} catch (<some exceptions> ex) {
ex.printStackTrace();
}
</code></pre>
<p>Le dernier exemple montre comment faire une requête asynchrone cette fois.
En utilisant les <em>lambdas</em>, c'est impressionnant comme c'est facile à écrire.</p>
<pre><code class="language-java">
try {
HttpClient client = HttpClient
.newBuilder()
.version(HttpClient.Version.HTTP_2)
.build();
HttpRequest request = HttpRequest
.newBuilder(new URI("https://pit.namok.be"))
.GET()
.build();
Path tempFile = Files.createTempFile("http2-test", ".html");
CompletableFuture<HttpResponse<Path>> futureResponse = client
.sendAsync(request,
HttpResponse.BodyHandler.asFile(tempFile))
.orTimeout(2000, TimeUnit.MILLISECONDS)
.whenComplete((r, e)
-> System.out.printf("Callback status %d %s\n",
r.statusCode(),
r.body()))
.exceptionally((e) -> {
System.out.printf("Exceptionally %s\n",
e.getClass());
return null;
});
futureResponse.join();
} catch (IOException
| URISyntaxException ex) {
ex.printStackTrace();
}
</code></pre>
<p>Notez que le client HTTP est un « module incubateur » (<em>incubator module</em>). Ce
qui signifie que:</p>
<ul>
<li><p>le module est appelé <code>jdk.incubator.httpclient</code> et les classes
<code>jdk.incubator</code> <code>.http.Http*</code>. Ce module doit être ajouté par le biais d'un
fichier <code>module-info</code> qui aura la forme suivante:</p>
<pre><code class="language-java">
module pbt.trys {
requires jdk.incubator.httpclient;
}
</code></pre></li>
<li><p>les classes qui incubent ne se trouveront pas dans Java 10 et peuvent être
modifiées. Dans ce cas précis, le module s'appellera <code>java.httpclient</code> et les
classes (probablement) <code>java.http.Http*</code>.</p></li>
</ul>
<h2 id="inputstream">La classe <em>InputStream</em> se dote également de nouvelles méthodes</h2>
<p>Trois nouvelles méthodes utilitaires pour la classe <code>InputStream</code>:
<code>readAllBytes</code>, <code>readNBytes</code> et <code>transferTo</code>.</p>
<p>Ces méthodes appellent peu de commentaires. Les deux premières permettent la lecture
d'un ficher dans un tableau de <em>bytes</em> et sont plutôt destinées aux petits
fichiers et la dernière sert à envoyer directement le flux d'entrée vers un flux
de sortie.</p>
<h2 id="flow">La classe <code>Flow</code>, le producteur et le consommateur</h2>
<p>Ce <em>design pattern</em> est très simple; un producteur qui produit des tâches, un
consommateur qui exécute les tâches une par une et une file d'attente dans
laquelle le producteur ajoute ses tâches. Le consommateur les retire une par
une. Il peut bien sûr y avoir plusieurs producteurs et plusieurs consommateurs.</p>
<p>C'est la gestion de la file d'attente qui doit être soignée. Elle
est en forte concurrence d'accès. Actuellement, si je devais coder ce <em>design</em>,
je mettrais l'accès à la file d'attente en section critique (via un
<em>synchronized</em>, ou en utilisant une <code>BlockingQueue</code>).</p>
<p>Java 9 introduit une nouvelle classe <code>Flow</code> dans le <em>package</em>
<code>java.util.concurrent</code> pour une mise en œuvre de ce <em>pattern</em> <strong>producteur
/ consommateur</strong>. Cette classe <code>Flow</code> propose trois interfaces;
<code>Flow.Publisher<T></code>, <code>Flow.Subscriber<T></code> et <code>Flow.Subscription</code></p>
<p>J'écris une classe implémentant <code>Subscriber<T></code> pour le consommateur. Cette
interface contient quatre méthodes assez naturelles; une pour s'enregistrer, une
pour faire le boulot, une pour gérer les erreurs et la dernière pour clôturer.
Je peux écrire un code à l'allure suivante:</p>
<pre><code class="language-java">
public class VideoSubscriber implements Subscriber<Video> {
private Subscription subscription;
@Override
public void onSubscribe(Subscription subscription) {
this.subscription = subscription;
this.subscription.request(1);
}
@Override
public void onNext(Video v) {
System.out.println("I publish " + v.getTitle()
+ " from " + v.getAuthor());
v.publish();
subscription.request(1);
}
@Override
public void onError(Throwable arg0) {
System.err.printf("Error (%s)\n", arg0.getMessage());
System.exit(1);
}
@Override
public void onComplete() {
System.out.printf("Done\n");
}
}
</code></pre>
<p>Le producteur doit implémenter la classe <code>Publisher<T></code>. Il existe déjà une
classe l'implémentant. Il s'agit de <code>SubmissionPublisher<T></code>. Cette classe
propose — entre autres — une méthode <code>submit</code> qui soumet une tâche. Je peux donc
directement écrire un <em>main</em> à l'allure suivante:</p>
<pre><code class="language-java">
SubmissionPublisher<Video> publisher = new SubmissionPublisher<>();
VideoSubscriber subscriber = new VideoSubscriber();
publisher.subscribe(subscriber);
Videos.getRandomVideos(12).stream()
.forEach(v -> publisher.submit(v));
// wait consummer end
try {
Thread.sleep(5000);
System.exit(0);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
publisher.close();
</code></pre>
<p><em>Teaser</em> Java 10. Je pourrai bientôt écrire:</p>
<pre><code class="language-java">
var publisher = new SubmissionPublisher<Video>();
var subscriber = new VideoSubscriber();
</code></pre>
<p>… mais chut :-)</p>
<p>En attendant, essayez d'ajouter des consommateurs et des producteurs. Toute la
gestion est faite par Java.</p>
<h2 id="more">Quelques points non détaillés</h2>
<ol>
<li><p><em>Multi-release jar files</em> (<a href="http://openjdk.java.net/jeps/238">JEP238</a>);</p>
<p>Complète la structure des fichiers jar et autorise plusieurs <em>releases</em>
différentes pour une classe. Par exemple JDK9 et <JDK9.</p></li>
<li><p>annotation <code>@Deprecated</code>;</p>
<p>L'annotation se complète de paramètres. Par exemple la classe
<code>java.lang.Compiler</code></p>
<pre><code class="language-java">
package java.lang;
/**
* The {@code Compiler} class is provided to support
* Java-to-native-code compilers and related services.
* By design, the {@code Compiler} class does
* …
*/
@Deprecated(since="9", forRemoval=true)
public final class Compiler {
…
}
</code></pre></li>
<li><p>le moteur de rendu JavaFX et Java 2D est <em>Marlin</em> et plus <em>Pisces</em> ni
<em>Ductus</em> (<a href="http://openjdk.java.net/jeps/265">JEP265</a>);</p></li>
<li><p>le <em>garbage collector</em> (ramasse-miettes) sera G1 et plus <em>Parallel GC</em>
(<a href="http://openjdk.java.net/jeps/248">JEP248</a>);</p></li>
<li><p>à noter que l'on peut écrire des méthodes privées dans les interfaces… utiles
aux <em>default methods</em> (<a href="http://openjdk.java.net/jeps/213">JEP213</a>);</p></li>
<li><p>l'API <code>Applet</code> est dépréciée <em>@Deprecated(since = "9")</em> (<a href="http://openjdk.java.net/jeps/289">JEP289</a>);</p></li>
<li><p>suppression de certains outils obsolètes ou ajoutés à l'époque comme simples
outils de démonstration; <em>hprof</em>, <em>jhat</em> (<a href="http://openjdk.java.net/jeps/240">JEP240</a>, <a href="http://openjdk.java.net/jeps/241">JEP241</a>);</p></li>
</ol>
<p></br></p>
<p>Pour aller plus loin, vous pouvez consulter les
<a href="https://docs.oracle.com/javase/9/whatsnew/toc.htm">changements complets et classés chez Oracle</a>. Et reprendre ensuite une activité normale.</p>
<p></br></p>
<p></br></p>
<p>Crédit photo chez <a href="http://deviantart.com">DeviantArt</a> par <a href="https://pixelduster.deviantart.com/art/Unzipped-Egg-33169606">Pixel duster</a>. Un « neuf » pour Java 9. Bon d'accord.</p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Je ne vérifie pas à proprement parler hein ! J'illustre la curiosité qui
permet de comprendre les choses et qui montre que rien n'est magique et que
toutes les briques se mettent bien en place. Vous pouvez faire pareil. <a href="https://blog.namok.be/?post/2018/03/22/JDK9#fnref:f1" rev="footnote">↩</a></p>
</li>
<li id="fn:f2">
<p>Comme vous avez déjà décompressé les sources… <a href="https://blog.namok.be/?post/2018/03/22/JDK9#fnref:f2" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Apache derby, SQL interpréteururn:md5:e8eb193ebd20c85b5de1fd66d69eea0f2018-01-31T23:12:00+01:002018-02-13T08:12:10+01:00PiTMes doigts dans le clavieresigeekjava<p><a href="https://blog.namok.be/public/images/divers/2018/bois-beloeil.jpg"><img src="https://blog.namok.be/public/images/divers/2018/bois-beloeil.jpg" alt="" style="margin: 0 auto; display: block;" /></a></p>
<p><strong>Apache derby</strong> est un SGBD (Système de gestion de base de données) <em>open
source</em> implémenté en Java et disponible sous les termes de la licence Apache
v2.0.</p>
<p>Voici quelques avantages — simplement traduits du <a href="http://db.apache.org/derby/index.html">site officiel du projet</a>:</p>
<ul>
<li><p><em>derby</em> a un légère empreinte de l'ordre de 3,5Mib pour le moteur de base et
le <em>driver</em> JDBC embarqué;</p></li>
<li><p><em>derby</em> est basé sur Java, JDBC et les standards SQL;</p></li>
<li><p><em>derby</em> fournit un <em>driver</em> JDBC embarqué qui vous permet d'embarquer <em>derby</em>
dans n'importe quel solution Java;</p></li>
<li><p><em>derby</em> supporte également le modèle client/serveur habituel avec le driver
<em>Derby Network Client JDBC</em> et <em>Derby Network Server</em>;</p></li>
<li><p><em>derby</em> est facile à installer, déployer et utiliser.</p></li>
</ul>
<p>Jusque là, rien de bien neuf. Toute personne ayant développé en Java a utilisé
<em>derby</em>. Écrire du code utilisant JDBC et <em>derby</em> est assez simple. Ce n'est
pas l'objet de ce <em>post</em>. Une fois une base de données <em>derby</em> créée et
embarquée dans un projet, je voudrais l'interroger sans écrire du code Java.
Je voudrais une application dans mon terminal, comme <em>mysql</em> ou <em>psql</em>.</p>
<p><strong>ij</strong> est un outil permettant les requêtes SQL fourni avec <em>derby</em> et <a href="http://db.apache.org/derby/papers/DerbyTut/ij_intro.html">présenté
sur le site d'Apache</a>. Pour l'utiliser, il suffit de récupérer les <em>jars
qui vont bien</em> (<a href="http://db.apache.org/derby/derby_downloads.html">download</a>):</p>
<ul>
<li><code>derby.jar</code> pour le driver (par exemple le driver <em>embedded</em>). Mais ça,
c'est assez habituel et;</li>
<li><code>derbytools.jar</code> pour la classe <code>ij</code>.</li>
</ul>
<p>Après l'installation de <em>ij</em>, les <em>jars</em> se trouvent dans le sous-répertoire de
l'installation. Il suffit de les intégrer à sa variable d'environnement
<code>CLASSPATH</code>.</p>
<pre class="brush: bash">
$ export CLASSPATH=$CLASSPATH:/opt/db-derby/lib/derbytools.jar
:/opt/db-derby/lib/derby.jar
:/opt/db-derby/lib/derbyrun.jar
</pre>
<p>L'interpréteur SQL est une simple classe java. Une fois lancée, on obtient un
interpréteur SQL.</p>
<pre class="brush: bash">
$ java org.apache.derby.tools.ij
Version IJ 10.14
ij>
</pre>
<p><a href="http://db.apache.org/derby/papers/DerbyTut/ij_intro.html">Le tutoriel d'introduction fourni par Apache</a> se résume en quelques
commandes.</p>
<pre class="brush: bash">
ij> connect 'jdbc:derby:mydb;create=true';
ij> connect 'jdbc:derby:mydb';
</pre>
<p>Ces deux commandes se connectent à la bd. La première la créée si elle
n'existe pas tandis que la seconde suppose que la bd existe (dans le
répertoire courant).</p>
<p>Une fois connecté, je peux entrer des commandes SQL habituelles. Supposons
l'existence d'un fichier <code>my.sql</code> dont voici le contenu:</p>
<pre class="brush: sql">
create table MY
(
id DECIMAL(5) not null primary key,
digits VARCHAR(32),
digitd DECIMAL(1)
);
INSERT INTO MY VALUES(1,'zéro',0);
INSERT INTO MY VALUES(2,'un',1);
INSERT INTO MY VALUES(3,'deux',2);
INSERT INTO MY VALUES(4,'trois',3);
INSERT INTO MY VALUES(5,'quatre',4);
INSERT INTO MY VALUES(6,'cinq',5);
INSERT INTO MY VALUES(7,'six',6);
INSERT INTO MY VALUES(8,'sept',7);
INSERT INTO MY VALUES(9,'huit',8);
INSERT INTO MY VALUES(10,'neuf',9);
</pre>
<p>Supposons éqalement qu'aucune bd n'existe, une session pourrait ressembler à:</p>
<pre class="brush: bash">
ij> connect 'jdbc:derby:mydb;create=true';
ij> run 'my.sql';
ij> create table MY
(
id DECIMAL(5) not null primary key,
digits VARCHAR(32),
digitd DECIMAL(1)
);
0 lignes insérées/mises à jour/supprimées
ij> INSERT INTO MY VALUES(1,'zéro',0);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(2,'un',1);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(3,'deux',2);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(4,'trois',3);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(5,'quatre',4);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(6,'cinq',5);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(7,'six',6);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(8,'sept',7);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(9,'huit',8);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(10,'neuf',9);
1 ligne insérée/mise à jour/supprimée
ij> select * from my;
ID |DIGITS |DIG&
--------------------------------------------
1 |zéro |0
2 |un |1
3 |deux |2
4 |trois |3
5 |quatre |4
6 |cinq |5
7 |six |6
8 |sept |7
9 |huit |8
10 |neuf |9
10 lignes sélectionnées
ij> exit;
</pre>
<p><em>That's all folks</em>. Voilà pour la base.</p>
<p><br/></p>
<p><em>Crédit photo personnelle.</em></p>
Installation de cachet, the open source status page systemurn:md5:c575078f1fc648cee85491e5b5835afc2017-11-09T11:20:00+01:002017-11-09T11:43:46+01:00PiTMes doigts dans le clavierdebianesigeekgitlogiciellibre<p><img src="https://blog.namok.be/public/images/divers/2017/jonathan-perez-409943-1200x.jpg" alt="jonathan-perez-409943-1200x.jpg" style="margin: 0 auto; display: block;" title="jonathan-perez-409943-1200x.jpg, nov. 2017" /></p>
<p><strong>Cachet</strong> est un système qui offre une page de statut de différents services
web. Il permet de reporter rapidement et de centraliser l'état des services
web d'une société / asbl / groupement…</p>
<p>Pour l'école, ça se trouve là:
<a href="http://status.esi-bru.be">http://status.esi-bru.be</a></p>
<p>L'installation de <strong>cachet</strong> se fait en suivant <a href="https://docs.cachethq.io/docs/installing-cachet">la documentation
officielle</a>… à quelques changements près.</p>
<p>Pour le début, je suis simplement le tuto.</p>
<pre>
cd /var/www/html
git clone https://github.com/cachethq/Cachet.git
cd Cachet
git tag -l
git checkout v2.3.13
cp .env.example .env
vim .env
</pre>
<p>Édition du fichier de configuration. Comme ce sera une BD <em>postgresql</em>, il
faut — l'installer si ce n'est fait — créer la BD et créer un utilisateur
dédié: <em>cachet</em>. Pour ce faire:</p>
<ol>
<li><p>création de la BD</p>
<pre>
sudo -u postgres createdb cachet
</pre></li>
<li><p>choisir un utilisateur</p>
<pre>
sudo -u postges createuser --interactive
</pre></li>
<li><p>lui attribuer un mot de passe — <code>pwgen 10 -1</code> fait bien l'affaire — que je peux reporter dans le fichier de configuration</p>
<p></p>
<pre>
psql (9.6.3)
Saisissez « help » pour l'aide.
postgres=# \password cachet
Saisissez le nouveau mot de passe :
Saisissez-le à nouveau :
postgres=# \q
</pre></li>
</ol>
<p>Pour l'installation de <em>composer</em>, je préfère faire confiance à mon
gestionnaire de paquets et faire un <code>apt install composer</code> disponible
depuis <em>stretch</em> sous <em>debian</em>. La génération de la clé nécessite que
<em>composer</em> soit installé. Ensuite, l'installation de <em>cachet</em> se passe sans
soucis.</p>
<pre>
composer install
php artisan key:generate
php artisan app:install
</pre>
<p>Il est temps d'ajouter le <em>vhost</em> <em>apache2</em> — ou <em>nginx</em> — et d'activer <em>mod
rewrite</em>.</p>
<pre>
a2enmod rewrite
</pre>
<pre>
<VirtualHost *:80>
ServerName status.example.org
ServerAlias www.status.example.org
ServerAdmin webmaster@example.org
DocumentRoot /var/www/html/cachet/public
<Directory "/var/www/html/cachet/public">
Require all granted
# Used by Apache 2.4
Options Indexes FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
</pre>
<p>À ce stade, <em>cachet</em> est installé… mais n'a que peu d'interêt. Il devient
utile s'il est capable de se mettre à jour sans intervention humaine. Après
lecture de <a href="https://docs.cachethq.io/reference">la documentation</a>, je comprends que <em>cachet</em> propose une API
et que je peux le mettre à jour via… un script <em>python</em> par exemple.</p>
<pre>
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
#
import requests
import json
url= "http://status.example.org/api/v1/ping"
response = requests.request("GET", url)
parsed = json.loads(response.text)
print json.dumps(parsed)
</pre>
<p>Pour aider à écrire les requêtes, j'utilise la librairie
<a href="https://github.com/dmsimard/python-cachetclient">python-cachetclient</a> qui s'installe par une des deux commandes suivantes:</p>
<pre>
pip3 install python-cachetclient
pip3 install git+https://github.com/dmsimard/python-cachetclient
</pre>
<p>Les <a href="https://github.com/dmsimard/python-cachetclient/blob/master/contrib/example.py">examples</a> sont clairs et j'utilise — comme version 1 — le script
à l'allure suivante:</p>
<pre>
#!/usr/bin/env python3
#
# Python script for status.example.org
#
# Author: Pierre BT
# Thanks to cahethq and python-cachetclient
#
import cachetclient.cachet as cachet
import json
import requests
ENDPOINT = 'http://status.example.org/api/v1'
API_TOKEN = '140if12345ABCDefghijkl'
SATUS_OK = 1
SATUS_PERF_ISSUES = 2
SATUS_PARTIAL_OUTAGE = 3
INCIDENT_INVESTIGATING = 1
INCIDENT_IDENTIFIED = 2
INCIDENT_WATCHING = 3
INCIDENT_FIXED = 4
SITEWEB = 'http://example.org'
SITEWEB_ID = 1
def ping_site_web(site, components_id, components, incidents):
""" Ping site web and update status
"""
try:
request = requests.get(site)
if request.status_code == 200:
components.put(id=components_id, status=SATUS_OK)
else:
components.put(id=components_id, status=SATUS_PERF_ISSUES)
incidents.post(
name='Erreur ' + str(request.status_code)
+ ' sur ' + site,
message=('Tout le monde est sur le pont pour régler ça…'
'et ce message est automatique.'),
status=INCIDENT_INVESTIGATING)
except Exception as e:
components.put(id=SITEWEB_ID, status=SATUS_PARTIAL_OUTAGE)
incidents.post(
name='Problème grave sur le ' + site,
message=('Tout le monde est sur le pont pour régler ça… '
'et ce message est automatique.'),
status=INCIDENT_INVESTIGATING)
#
# main method
#
components = cachet.Components(endpoint=ENDPOINT, api_token=API_TOKEN)
incidents = cachet.Incidents(endpoint=ENDPOINT, api_token=API_TOKEN)
ping_site_web(SITEWEB, SITEWEB_ID, components, incidents)
</pre>
<p>Il reste à ajouter ce script dans une tache <strong>cron</strong> et la première
automatisation — certes assez basique — est en place.</p>
<p>Enjoy et merci aux <em>devs</em> !</p>
<p><br/></p>
<p><em>Crédit photo chez <a href="http://unsplash.com">Unsplash</a>par <a href="https://unsplash.com/photos/6BzGt4OXzeM">Jonathan Perez</a>. Les
grincheux·ses pourraient dire que c'est facile d'utiliser une photo d'un
boite de cachets… mais ce sont des grincheux·ses non ?</em></p>
Nouveau certificat chez Gandi, confiance et giturn:md5:e9030e49ff42f3bd879904af38f42e492017-06-26T11:16:00+02:002017-07-15T12:27:30+02:00PiTCartable au dosdebianesigeekgit<p><img src="https://blog.namok.be/public/images/divers/2017/mac-laughtguy-408H-1200x.jpg" alt="mac-laughtguy-408H-1200x.jpg" style="margin: 0 auto; display: block;" title="mac-laughtguy-408H-1200x.jpg, juin 2017" /></p>
<p>Pour le serveur gitlab de l'école, je viens de renouveler les certificats chez
Gandi. Si l'on se connecte via son navigateur, on a un beau certificat un peu
plus récent:</p>
<p><code>TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 256 bits</code></p>
<p>Par contre, lorsque j'essaie un <em>git clone | pull | push</em> rien ne va plus car
l'autorité n'est pas reconnue par ma machine. Il faut que j'aille chercher le
certificat sur le serveur ou bien chez Gandi et que je l'ajoute à ma liste
de certificats.</p>
<p>Pour prendre le certificat <strong>sur le serveur</strong>, je fais donc:</p>
<p><code>echo -n | openssl s_client -showcerts -connect git.esi-bru.be:443</code></p>
<p>… et je mets les deux chaines entre <code>-----BEGIN CERTIFICATE----</code> et <code>-----END
CERTIFICATE-----</code> dans un fichier <code>MyGandi.crt</code></p>
<p>Si je préfère aller chercher le certificat <strong>chez Gandi</strong> directement, il se
trouve <a href="https://wiki.gandi.net/fr/ssl/intermediate">à cette page</a> et cette
commande devrait faire l'affaire</p>
<p><code>wget https://www.gandi.net/static/CAs/GandiStandardSSLCA2.pem</code></p>
<p>Une fois en possession du certificat, il faut le donner à votre machine via
ces deux commandes:</p>
<p><code>cp /elsewhere/GandiStandardSSLCA2.pem /usr/local/share/ca-certificates/GandiStandardSSLCA2.crt
update-ca-certificates</code></p>
<p>Notez qu'il est nécessaire de changer l'extension du fichier pour qu'il soit
pris en compte par <code>update-ca-certificates</code>. Cette deuxième commande va
concaténer tous les certificats présents dans <code>/etc/ssl/certs</code> et
<code>/usr/local/share/</code> pour les rassembler dans le fichier
<code>/etc/ssl/certs/ca-certificates</code>.</p>
<p>Et ça roule…</p>
<p><strong>Édité</strong></p>
<p>Bon, savoir reconnaitre ses erreurs… bla bla. Dans mon fichier <code>.pem</code>, quelques lettres s'étaient enfuies dans la bagarre et le fichier n'était pas correct. Cette manipulation n'est donc pas utile. Désolé pour les désagréments.</p>
<p><br/></p>
<p><em>Crédit photo chez <a href="http://gratisography.com/">Gratisography</a>.</em></p>
Commencer par une majuscule… oui mais non.urn:md5:31b1d6592d1263f76119713eb5b5c6092016-11-23T12:54:00+01:002016-12-19T09:04:43+01:00PiTMes doigts dans le clavierbépoesiinutile<p><img src="https://blog.namok.be/public/images/divers/2016/clavier-maindoigtplie-cut.jpg" alt="clavier-maindoigtplie-cut.jpg" style="margin: 0 auto; display: block;" title="clavier-maindoigtplie-cut.jpg, nov. 2016" /></p>
<p>Lorsque j'écris, je fais attention à mon orthographe bien qu'elle soit
médiocre, j'accentue mes lettres majuscules depuis que j'ai <a href="http://namok.be/blog/?post/2012/11/22/touches-compose-et-altgr">découvert la
touche compose</a>, je différencie un tiret d'un tiret quadratin, <a href="http://namok.be/blog/?post/2009/03/05/226-nouvelle-orthographe">je
préfère l'orthographe réformée</a> — non ! On ne dit plus « la nouvelle
orthographe » dès lors que vingt ans sont passés — à l'ancienne orthographe,
<a href="http://namok.be/blog/?post/2010/11/09/Epsilon-2010">j'aère mon texte à la mode <em>information mapping</em></a>… bref, j'essaie de
respecter les règles du français pour que les documents que je rédige soient
clairs et propres. S'ils le sont, ils seront mieux compris.</p>
<p>Quand je communique par mail ou par messagerie instantanée, je m'efforce
également d'aérer mon texte en n'hésitant pas à ajouter des sauts de ligne et
des passages à la ligne et en utilisant des listes à puces. J'estime respecter
mon interlocuteur si mon texte est rapidement lisible.
Parfois je reçois une réponse sans aucun passage à la ligne, c'est simplement
illisible… mais je ne renonce pas pour autant.</p>
<p>En parlant de <a href="http://namok.be/blog/?post/2007/12/06/netiquette">la netiquette</a> — je t'invite à <a href="http://www.faqs.org/rfcs/rfc1855.html">(re)lire la RFC
1855</a> — j'y lis en anglais d'utiliser; <em>mixed case</em><sup id="fnref:f1"><a href="https://blog.namok.be/?post/2016/11/23/commencer-par-une-majuscule-oui-mais-non#fn:f1" rel="footnote">1</a></sup> tandis
que dans sa traduction française, on y parle d'écrire en minuscules.</p>
<blockquote>
<ul>
<li><p>Use mixed case. UPPER CASE LOOKS AS IF YOU'RE SHOUTING.</p></li>
<li><p>Écrivez normalement en minuscule. UTILISER LES MAJUSCULES REVIENT À CRIER.</p></li>
</ul>
</blockquote>
<p>Bref, l'idée est d'écrire tout aussi normalement lorsque l'on écrit un texte
destiné à être publié que lorsque l'on rédige un mail ou quand on trolle sur
les réseaux sociaux et autres messageries instantanées… et pourtant.</p>
<p>La netiquette parle de <em>talk</em> — l'ancêtre de toutes les messageries
instantanées — en nous rappelant deux, trois petites choses:</p>
<ul>
<li><p><em>N'oubliez pas que le talk équivaut à une interruption de votre
interlocuteur. Ne l'utilisez que dans les règles. Et ne parlez jamais à un
étranger.</em></p>
<p>Rien de neuf sous le soleil, ça fait donc vingt ans qu'on le dit ! Si
connaitre ses interlocuteurs est un message qui commence à rentrer, la
notion d'interruption semble complètement mise de côté. Il suffit
d'observer certains utilisateurs pour se rendre compte qu'il passe
beaucoup plus de temps à être interrompu qu'à faire avancer leur travail.</p>
<p>Mais je m'égare…</p></li>
<li><p><em>Talk trahit votre dextérité au clavier. Si vous tapez lentement et faites
des erreurs, ce n'est souvent pas la peine de corriger car votre
interlocuteur peut comprendre ce que vous voulez dire.</em></p>
<p>Il est habituel de ne pas recopier une phrase dès que l'on fait une typo.
Certains on prit l'habitude de corriger un mot fautif en le faisant
précéder d'une étoile sans recopier toute la phrase.</p>
<p>Ça me parrait bien.<br />
*parait</p></li>
<li><p><em>Utilisez normalement les minuscules et la ponctuation, comme si vous tapiez
une lettre ou un courrier électronique.</em></p>
<p>Alors ? <em>mixed case</em> ou complètement en minuscules ?</p></li>
</ul>
<p>Je souffre — je sais que je rabâche<sup id="fnref:f2"><a href="https://blog.namok.be/?post/2016/11/23/commencer-par-une-majuscule-oui-mais-non#fn:f2" rel="footnote">2</a></sup> — de troubles musculosquelettiques et
plus précisément d'une inflammation de l'extenseur du cinquième. Précisément
parce que j'ai pris l'habitude d'entrer les chiffres et les majuscules en
appuyant sur la touche [Shift] avec le petit doigt de la même main que ceux
qui appuient sur les touches correspondant aux caractères à écrire en
majuscules.</p>
<p><strong>Netiquette 2.0</strong> J'écris <strong>complètement en minuscules</strong> — sans majuscule en
début de mot donc:</p>
<ul>
<li><p>pour la messagerie instantanée;</p>
<p>Ce qui ne va pas perturber mes amis, ils en ont déjà l'habitude.</p></li>
<li><p>pour mes commentaires de correction dans les codes des étudiants.</p>
<p>Une petite explication de la raison et ça roule…</p></li>
<li><p>pour les mails.</p>
<p>Ce n'est absolutemnt pas un manque de politesse ou de respect. Je continue
évidemment à: aérer le texte, dire bonjour, terminer par une formule de
politesse, soigner l'orthographe, couper / citer pour raccourcir les
messages, essayer d'être bref sans être sec, couper les lignes, écrire un
sujet représentatif du contenu…</p></li>
</ul>
<p>Mon prochain mail aura donc l'allure suivante:</p>
<p><tt>
bonjour,<br />
<br/>
conformément à nos habitudes nous allons faire un affichage groupé pour
annoncer la prochaine interro. pour rappel, elle a lieu la semaine du 21
novembre.</p>
<p>pouvez-vous compléter le <em>pad</em> suivant: http://framapad.org/p/something.</p>
<p>bàv<br />
juste
</tt></p>
<p><br/></p>
<p><em>Photo personnelle quand j'essaie de montrer que l'auriculaire est plié
lorsque j'enfonce la touche [Shift].</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Casse mixte, c'est-à-dire écrire en minuscules et en mettant des
majuscules en début de mot comme on a l'habitude de faire. <a href="https://blog.namok.be/?post/2016/11/23/commencer-par-une-majuscule-oui-mais-non#fnref:f1" rev="footnote">↩</a></p>
</li>
<li id="fn:f2">
<p>J'insiste pour que les jeunes lecteurs prennent garde dès maintenant
à leur position devant l'écran; frappe à 10 doigts avec une disposition
bépo au minimum et utilisation d'un clavier TypeMatrix si possible (ce que
je ne fais pas car je suis trop itinérant). <a href="https://blog.namok.be/?post/2016/11/23/commencer-par-une-majuscule-oui-mais-non#fnref:f2" rev="footnote">↩</a></p>
</li>
</ol>
</div>
LaTeX, package minitocurn:md5:8dcfc29296930f95309e64055252eca62016-03-05T23:55:00+01:002016-03-06T00:03:42+01:00PiTMes doigts dans le clavierdiversenseignementesigeeklatexlogiciellibremarkdownpandoc<p><img src="https://blog.namok.be/public/images/divers/2016/yellow_by_i_shadow.jpg" alt="yellow_by_i_shadow.jpg" style="margin: 0 auto; display: block;" title="yellow_by_i_shadow.jpg, mar. 2016" /></p>
<p>Lors d'un <a href="http://namok.be/blog/?post/2010/11/09/Epsilon-2010">salon</a> il y a quelques années déjà, j'ai découvert la méthode
<em><a href="http://information-mapping.fr">information mapping</a></em> pour mettre en forme des textes de manière telle qu'ils
soient plus lisibles, plus compréhensibles et (donc) mieux compris. La méthode est
<em>propriétaire</em>. C'est assez drôle d'ailleurs de voir comme l'info à ce sujet est
assez cadenassée. Il faut suivre les formations et les outils sont MS Windows.</p>
<p>Bref, j'en ai retenu six ans plus tard les points suivants:</p>
<ul>
<li>aérer le texte (interlignes plus grands);</li>
<li>ne pas hésiter à passer des lignes;</li>
<li>utiliser les <em>items</em> (liste à puces) et les tableaux dès que c'est possible; </li>
<li>ajouter une table des matières en début de chaque section.</li>
</ul>
<p>Je voudrais pouvoir ajouter facilement des tables des matières par section
lorsque j'utilise LaTeX.</p>
<p>Ça tombe bien, je rédige souvent en <a href="http://namok.be/blog/?post/2013/11/19/billet-markdown">Markdown</a> mes billets et mes notes de
cours par exemple. Lorsque ma sortie est en pdf et que c'est mouliné par
<a href="http://johnmacfarlane.net/pandoc/">pandoc</a>, je peux ajouter quelques commandes LaTeX directement dans mon
texte. Il est donc tout à fait possible d'utiliser le package <strong><a href="http://ctan.org/pkg/minitoc">MiniToc</a></strong>
qui ajoute, justement (comme le monde est bien fait) une mini table des
matières en début de section ou chapitre.</p>
<p>J'ajoute donc à mon entête que j'avais <a href="http://namok.be/blog/?post/2015/10/08/latex-markdown-wallpaper">déjà présentée</a> les lignes
suivantes dans mes <em>header-include</em>:</p>
<pre class="brush: plain">
- \usepackage{minitoc}
- \setcounter{secttocdepth}{3}
- \renewcommand{\stctitle}{}
</pre>
<p>En début de document, je précise que je vais demander des tables des matières
par çi par là et qu'il faut que LaTeX se prépare.</p>
<pre class="brush: plain">
\dosecttoc
\setcounter{tocdepth}{1}
\tableofcontents
</pre>
<p>Et lorsque je commence une section en Markdown, je fais suivre mon titre par une
commande Latex. Un peu comme suit:</p>
<pre class="brush: plain">
# Titre de niveau un
\secttoc
Bla bla
## Titre de niveau deux qui apparaitra dans la minitoc
</pre>
<p>Vous pouvez reprendre une activité normale.</p>
<p><br/></p>
<p><em>Crédit photo chez <a href="http://deviantart.com">DeviantArt</a> par <a href="http://muratsuyur.deviantart.com/art/yellow-69146298">muratsuyur</a>. Vous
prendrez bien une p'tite rondelle de citron dans votre blanche ?</em></p>
Borne d'arcadeurn:md5:d715181674be83e29985806f8568e4b92016-02-16T15:02:00+01:002016-02-16T15:07:12+01:00PiTMoidebianesigeek<p><img src="https://blog.namok.be/public/images/divers/2016/bornearcade/borne-1.jpg" alt="borne-1.jpg" style="margin: 0 auto; display: block;" title="borne-1.jpg, fév. 2016" /></p>
<p>J'ai eu la chance de récupérer une <strong>borne d'arcade</strong>.</p>
<p>Mes souvenirs de ces bornes se concentrent à la période que j'ai passée
à Louvain-la-neuve. Pas de télé au kot. Pas internet évidemment. Je passais
alors un peu de temps dans le lunapark<sup id="fnref:f1"><a href="https://blog.namok.be/?post/2016/02/16/borne-arcade#fn:f1" rel="footnote">1</a></sup> à mater ceux qui savaient jouer.
Soit parce que j'étais mauvais, soit parce que j'étais radin ;-) J'ai
visuellement le souvenir de pas mal de jeu et je ne me souviens de quasi aucun
nom.</p>
<p>Bref, quand on m'a proposé une borne d'arcade, je n'ai pas hésité<sup id="fnref:f2"><a href="https://blog.namok.be/?post/2016/02/16/borne-arcade#fn:f2" rel="footnote">2</a></sup>. Un
aller-retour au fin fond de la flandre et la borne se retrouve sous le préau.</p>
<p><img src="https://blog.namok.be/public/images/divers/2016/bornearcade/borne-3.jpg" alt="borne-3.jpg" style="margin: 0 auto; display: block;" title="borne-3.jpg, fév. 2016" /></p>
<p>D'abord tout vider car je ne veux pas me contenter d'un seul jeu et que je n'y
connais pas grand chose en électronique. Ensuite, essayer de récupérer un PC
et un écran. L'idée, c'est que ça ne coûte (presque) rien. Ce sera donc
principalement de la récup !</p>
<p>Je m'arrange pour récupérer un pc à l'école et, pour que la borne reste <em>vintage</em>,
je demande aussi un écran CRT.</p>
<p>— Je pourrais récupérer un viel écran 17' CRT ?<br />
— Tu sais les écrans, on les garde, ils passent d'un labo à l'autre.<br />
— Un CRT, pas un TFT !<br />
— Ah… t'en veux combien ?</p>
<p>La première chose à faire après le vidage (en fait, tu fais ça dans l'ordre
que tu veux), c'est de <strong>bien nettoyer</strong> les connecteurs des boutons et des
joysticks<sup id="fnref:f3"><a href="https://blog.namok.be/?post/2016/02/16/borne-arcade#fn:f3" rel="footnote">3</a></sup>. Il faut donc commencer par <strong>bien nettoyer</strong> les
connecteurs<sup id="fnref:f4"><a href="https://blog.namok.be/?post/2016/02/16/borne-arcade#fn:f4" rel="footnote">4</a></sup>. Quelques minutes et une feuille de papier de verre plus
tard, c'est réglé.</p>
<p><img src="https://blog.namok.be/public/images/divers/2016/bornearcade/borne-4.jpg" alt="borne-4.jpg" style="margin: 0 auto; display: block;" title="borne-4.jpg, fév. 2016" /></p>
<p>Comme je ne suis pas électronicien et que je ne m'intéresse pas trop aux
connecteurs Jamma, je vais devoir raccorder les boutons et les joystiks au pc.
Il y a deux manières de faire:</p>
<ul>
<li>le hack des manettes snes qui consiste à acheter des manettes, à les
ouvrir et à souder les fils des boutons et des joysticks sur la carte
électronique. Ensuite, brancher l'usb. Ça fonctionne et c'est pas très cher;</li>
<li>acheter le <em>THT arcade console 2P USB controler</em> (chez <a href="http://www.starcab.net/product_info.php?products_id=1013&osCsid=1ac116375106bf46ce197a84a21f407e">starcab</a> par
exemple) connecter les boutons et les joysticks. C'est propre, ça évite
les soudures et c'est quasi le même prix.</li>
</ul>
<p>Après avoir chipoté un peu avec la première solution, c'est finalement la
deuxième qui fonctionne bien pour moi… après avoir <strong>bien nettoyé</strong> les
connecteurs (sinon, ça fait court-cirtuit et la carte plante).</p>
<p>Emporté par mon élan, je décide d'ajouter un bouton afin d'accéder à quelques
<em>jeux de baston</em> à 4 boutons (il n'y a pas vraiment la place pour en mettre
6). Une scie cloche et une heure plus tard, les trous son faits.</p>
<p><img src="https://blog.namok.be/public/images/divers/2016/bornearcade/borne-5.jpg" alt="borne-5.jpg" style="margin: 0 auto; display: block;" title="borne-5.jpg, fév. 2016" /></p>
<p>Les boutons et les joysticks sont raccordés. Je scie un peu, coupe et raccorde
pour placer l'écran dans la borne, alimenter le <em>marquee</em> et raccorder le
baffle à un jack que je branche dans le pc.</p>
<p><img src="https://blog.namok.be/public/images/divers/2016/bornearcade/borne-6.jpg" alt="borne-6.jpg" style="margin: 0 auto; display: block;" title="borne-6.jpg, fév. 2016" /></p>
<p>Le pc.<br />
Reste le pc.</p>
<p>Pas trop de soucis avec lui. J'installe une petite <a href="http://debian.org">debian</a> avec l'émulateur
<a href="http://mamedev.org">mame</a>. Ce qui me prend le plus de temps, c'est la recherche des jeux qui
m'intéressent (je vous ai dit que je ne connaissais pas les noms) et des
<em>roms</em> qui fonctionnent bien (j'ai principalement été sur <a href="http://emuparadise.me">emuparadise</a>).</p>
<pre><code>apt install mame
apt install advancemenu
</code></pre>
<p>Deux nuits plus tard, c'était plié ;-)</p>
<pre><code>1941.zip 1943.zip 3wonders.zip acrobatm.zip aof3.zip aof.zip arkanoid.zip
arknoid2.zip atetrisb.zip athena.zip bbakraid.zip bgaregga.zip bldyroar.zip
bldyror2.zip bloxeed.zip bublbobl.zip chasehq.zip columns.zip crimfght2.zip
crimfght.zip daioh.zip ddragon2.zip diggerc.zip diggerma.zip dkong.zip
fatfury2.zip fatfury3.zip galaga88.zip galpanic.zip guwange.zip gyruss.zip
hangon.zip hharry.zip kof2002.zip ladykill.zip loht.zip mario.zip mrdrillr.zip
neogeo.zip notuse pang.zip pbobble2u.zip pbobble2.zip pbobblen.zip pdrift.zip
playch10.zip popnpop.zip psarc95.zip puzzli.zip radm.zip rtype.zip
sailormn.zip samsh5sp.zip samsho5.zip samshoh.zip samsho.zip sfiii3 sfiii3.zip
shanghaito.chd shanghss.chd shanhigw.zip shngmtkb.zip spacefev.zip stvbios.zip
superpac.zip supmodel.zip suprmria.zip suprmrio.zip svcpcba.zip taitogn.zip
tetris tgm2.zip tps.zip truxton2.zip volfied.zip
</code></pre>
<p>J'utilise <em>advancemenu</em> (dont la commande est <code>advmenu</code> il faut le savoir ^^)
comme interface plus conviviale que celle de <em>mame</em>.</p>
<p>Tout est fonctionnel, je peux jouer.</p>
<p><img src="https://blog.namok.be/public/images/divers/2016/bornearcade/borne-7.jpg" alt="borne-7.jpg" style="margin: 0 auto; display: block;" title="borne-7.jpg, fév. 2016" /></p>
<p>La prochaine étape est le remplacement de <em>debian / mame /advancemenu</em> par
<em><a href="http://lakka.tv">Lakka</a></em>, une distribution clef sur porte pour l'émulation de jeux.</p>
<p>Ce sera pour demain…</p>
<p><br/></p>
<p><em>Les photos sont de moi.</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Mon correcteur ne connait pas <em>lunapark</em> mais il connait <em>lupanars</em>.
Comme quoi. <a href="https://blog.namok.be/?post/2016/02/16/borne-arcade#fnref:f1" rev="footnote">↩</a></p>
</li>
<li id="fn:f2">
<p>Merci Waness, en me la donnant, on a fait plaisir à ton papa ;-) <a href="https://blog.namok.be/?post/2016/02/16/borne-arcade#fnref:f2" rev="footnote">↩</a></p>
</li>
<li id="fn:f3">
<p>On me l'avait effectivement dit… et ça n'a pas percuté. J'ai perdu pas
mal de jours la-dessus en me demandant pourquoi les boutons et le joystick
2 ne fonctionnaient pas. Le coup de papier de verre magique de Bardack en aura
raison. <a href="https://blog.namok.be/?post/2016/02/16/borne-arcade#fnref:f3" rev="footnote">↩</a></p>
</li>
<li id="fn:f4">
<p>Comme ça n'avait pas percuté chez moi, je répète :-) <a href="https://blog.namok.be/?post/2016/02/16/borne-arcade#fnref:f4" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Représentation d'un JavaBean graphique avec Swing ou JavaFXurn:md5:8d8d5c8382d40c736184672ab767377e2015-11-09T11:35:00+01:002015-11-11T00:22:14+01:00PiTCartable au dosesigeekjava<p><img src="https://blog.namok.be/public/images/divers/2015/redleds-14339698018_8de0dd2659_o.jpg" alt="redleds-14339698018_8de0dd2659_o.jpg" style="margin: 0 auto; display: block;" title="redleds-14339698018_8de0dd2659_o.jpg, nov. 2015" /></p>
<p>Depuis <a href="http://namok.be/blog/?post/2014/06/03/jdk8">JDK8</a>, la librairie <a href="http://docs.oracle.com/javase/tutorial/uiswing/TOC.html">Swing</a> est devenue obsolète et est remplacée par
<a href="http://docs.oracle.com/javase/8/javase-clienttechnologies.htm">JavaFX</a>. Il existe moult tutoriels présentant JavaFX. Cet article ne sera
pas le <em>moult plus unième</em>.</p>
<p>En très bref, là où Swing s'organise avec un <code>JFrame</code> contenant des <code>JPanel</code>
— auxquels on associe un <em>layout manager</em> — eux-mêmes contenant divers composants/
graphiques tels que des <code>JButton</code>, <code>JTextField</code>, <code>JLabel</code>… tous dans le
package <code>javax.swing.*</code>, JavaFX se compare à un théâtre.</p>
<p>Le <code>Stage</code>, c'est le théâtre où se joue la pièce. La pièce se joue sur la
<code>Scene</code>. La scène est affublée d'un <em>layout</em> — <code>BorderPane</code> par exemple — qui
est à la fois l'ancien <em>panel</em> et son <em>layout manager</em>. C'est ce <em>layout</em> qui
contient les divers composants graphiques tels que des <code>Button</code>, <code>TextField</code>,
<code>Label</code>… tous dans les (sous)-packages <code>javafx.*</code>.</p>
<p>C'est beaucoup plus romantique — en tout cas imagé — comme présentation.</p>
<p>À l'aide d'un IDE, il est assez simple de planter le décors et d'y déposer ses
objets également. Les détails se trouvent dans divers tutoriaux. Ceux qui
préfèrent pourront utiliser <a href="http://docs.oracle.com/javafx/scenebuilder/1/use_java_ides/sb-with-nb.htm">Scene builder</a> au lieu d'un
positionnement « à la main ». Ce qui m'intéresse ici, c'est la gestion des
évènements. Est-elle (fort) différente qu'avec Swing ? D'ailleurs la suite est
plutôt destinée à ceux qui veulent voir la différence entre les deux
approches… ils pourront ensuite oublier l'approche « Swing » !</p>
<p>Le principe est identique, c'est le <em>design pattern</em> observateur / observé.
Certains composants ont la capacité d'être observés, d'autres celle
d'observer. Les observateurs doivent s'inscrire auprès de l'observé afin
d'être ajouté à la liste des observateurs et, ainsi, être notifiés des
changements.</p>
<p><em>La phrase précédente fonctionne très bien également avec le vocable écouteurs
/ écoutés.</em></p>
<p>Intéressons nous à la représentation graphique d'une LED électronique. <em>Ça
s'allume et ça s'éteint. C'est généralement rouge !</em> Cette led — je l'écrirai
toujours en minuscule même si c'est un acronyme pour <em>light emitting diode</em>
— possède une couleur et un état allumé (<em>on</em>) ou éteint (<em>off</em>). Sa couleur
et son état on/off sont deux propriétés que le <em>bean</em> présente. La led sera
représentée par un disque rouge lorsque elle est allumée et par un disque
blanc lorsqu'elle est éteinte. Sa couleur rouge par défaut pourra être
changée. Plus tard, ce composant graphique pourra être cliqué et changé d'état
on/off à chaque click de la souris.</p>
<p><em>Tous les codes présentés sont disponibles sur <a href="https://github.com/pbettens/leds">github</a>.</em></p>
<h1>Led</h1>
<h2>Un simple bean</h2>
<p>Comment créer un JavaBean ? Un javabean n'est pas nécessairement un composant
graphique. C'est une classe Java ayant certaines caractéristiques. La
principale étant que cet « objet » expose des propriétés.</p>
<p>Pour être reconnu comme étant un <em>bean</em>, il faut:</p>
<ul>
<li>être <code>Serializable</code>;</li>
<li>avoir un constructeur sans paramètre;</li>
<li>présenter correctement ses propriétés. Une propriété a un accesseur et un
mutateur. Dès lors que cette propriété change, le <em>bean</em> en informe tous ses
écouteurs. </li>
</ul>
<p>Avec Swing, il fallait hériter de <code>JPanel</code> et définir ses propriétés en
utilisant des chaines. Un peu comme ça.</p>
<p><em>Je retire sciemment la javadoc, les imports et l'instruction package des
codes exemples par soucis de concision. Pour plus de détails, voyez
<a href="https://github.com/pbettens/leds">github</a> comme je le propose plus haut.</em></p>
<pre class="brush: java; gutter: false;">
public class GLed extends JPanel
implements Serializable {
public static final String PROPERTY_ON = "my.package, property on";
public static final String PROPERTY_COLOR = "my.package, property color";
private Color color ;
private boolean on ;
public GLed() {
super();
this.setPreferredSize(/*set dimension here*/));
this.color = Color.RED;
this.on = false;
}
public Color getColor() {
return color;
}
public void setColor(Color newValue) {
if (newValue.equals(Color.WHITE)) {
throw new IllegalArgumentException(
"Invalid color (you try off color)");
}
Color oldValue = this.color;
this.color = newValue;
firePropertyChange(PROPERTY_COLOR, oldValue, newValue);
}
public boolean isOn() {
return on;
}
public void setOn(boolean newValue) {
boolean oldValue = this.on ;
this.on = newValue ;
this.firePropertyChange(PROPERTY_ON,oldValue,newValue);
}
}
</pre>
<p>Le <em>bean</em> se présente bien. Il a son constructeur sans paramètre qui permettra
d'instancier l'objet. À ses propriétés sont associées des accesseurs
/ mutateurs. Dès lors qu'une propriété change, il en informe ses écouteurs
via la méthode <code>firePropertyChange()</code> héritée de <code>JPanel</code>. Cet héritage lui
donne également les méthodes et attributs nécessaires pour gérer ses
écouteurs.</p>
<p>S'ajouter comme écouteur du led, se fait avec Swing, comme suit:</p>
<pre class="brush: java; gutter: false;">
led.addPropertyChangeListener(this);
</pre>
<p>Pour être écouteur, je dois être <code>PropertyChangeListener</code> et implémenter la
méthode <code>propertyChange</code>. Par exemple:</p>
<pre class="brush: java; gutter: false;">
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(GLed.PROPERTY_ON)) {
System.out.println("La led m'informe" + evt.getNewValue());
}
}
</pre>
<p>Il est bien sûr également possible d'ajouter un écouteur en utilisant une
classe interne anonyme ou nommée. Par exemple pour une classe interne anonyme:</p>
<pre class="brush: java; gutter: false;">
led.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(GLed.PROPERTY_ON)) {
System.out.println("La led m'informe" + evt.getNewValue());
}
}
});
</pre>
<p>La philosophie de JavaFX est un peu différente. En effet, JavaFX ajoute la
notion de « propriété » <em>via</em> des classes <code>Property</code>. Comme d'habitude les
classes existent pour les valeurs primitives et une classe <em>generics</em> existe
pour tous les autres objets.</p>
<p>Pour définir une led ayant comme propriété le booléen <em>on</em>, il suffira
d'écrire avec JavaFX:</p>
<pre class="brush: java; gutter: false;">
public class LedOn implements Serializable {
protected BooleanProperty on = new SimpleBooleanProperty(true);
public final void setOn(boolean aon){
on.set(aon);
}
public final boolean isOn(){
return on.get();
}
public final BooleanProperty onProperty(){
return on;
}
}
</pre>
<p>Déroutant n'est-ce pas ? Il est également nécessaire de s'ajouter comme
écouteur auprès de la led. La méthode <code>onProperty()</code> offre la gestion des
écouteurs tandis que la méthode <code>set()</code> se chargera du <em>fire</em> dès que c'est
nécessaire. Il n'est plus utile de s'en charger.</p>
<p>Pour pouvoir s'ajouter comme écouteur, on peut être
« <code>ChangeListener<Boolean></code> » et implémenter la méthode <code>changed()</code> ou
simplemen utiliser une classe interne anonyme comme suit:</p>
<pre class="brush: java; gutter: false;">
led.onProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
// I've changed. Promise
System.out.println("Led change de "
+ oldValue + " vers " + newValue);
}
});
</pre>
<p>Pour une valeur de type <code>Color</code> par exemple — qui n'est donc pas un type
primitif — c'est un peu plus complexe… quoique avec un IDE, le générateur de
code aide bien. En utilisant une classe interne anonyme (<em>inner class</em>),
l'ajout d'une propriété peut s'écrire:</p>
<pre class="brush: java; gutter: false;">
//…
protected ObjectProperty< Color> color =
new ObjectPropertyBase< Color>(Color.RED) {
@Override
public Object getBean() {
return this;
}
@Override
public String getName() {
return "Color property";
}
};
//…
public final Color getColor() {
return color.get();
}
public final void setColor(Color c){
color.set(c);
}
public final ObjectProperty< Color> colorProperty(){
return color;
}
</pre>
<p>L'ajout des <em>getter / setter</em> ainsi que la méthode <code>colorProperty()</code> se fait comme pour la propriété <em>on</em>. On a maintenant un <em>bean</em> fonctionnel et
exposant deux propriétés.</p>
<h2>Quand mon bean devient graphique</h2>
<p>Pour être un composant graphique avec Swing, le <em>bean</em> peut hériter de la classe
<code>javax.swing.JPanel</code>. En héritant de cette classe:</p>
<ul>
<li><p>j'hérite également de <code>javax.swing.Container</code> qui offre la capacité d'être
écouté. Je pourrai donc faire un <code>firePropertyChange()</code> dès qu'une propriété
change. C'est bien le développeur qui est responsable de ce <em>fire</em>;</p></li>
<li><p>je suis un composant <strong>graphique</strong> et il me suffit de réécrire la méthode
<code>paintComponent(Graphics)</code> pour que mon composant ait l'allure qui me
convient. Un appel à <code>repaint()</code> quand une propriété (graphique) change
permet la mise à jour de l'aspect graphique du composant.</p></li>
</ul>
<p>J'ajoute cette réécriture de la méthode <code>paintComponent()</code> dans mon code ainsi
qu'un appel à la méthode <code>repaint()</code> dans chaque <em>setter</em>.</p>
<pre class="brush: java; gutter: false;">
public class GLed extends JPanel
implements Serializable {
//…
public void setOn(boolean b) {
//…
repaint();
}
public void setColor(Color c){
//…
repaint();
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(
RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHint(
RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.BLACK) ;
g2.drawOval(/*…*/);
if ( isOn() ) {
g2.setColor(color) ;
} else {
g2.setColor(Color.WHITE) ;
}
g2.fillOval(/*…*/) ;
}
</pre>
<p>Pour être un composant graphique avec JavaFX, le bean peut hériter de la
classe <code>javafx.scene.Parent</code>. Pas de méthode <code>paintComponent()</code> appelée
automatiquement ou <em>via</em> <code>repaint()</code>. Il suffit d'ajouter des composants
graphiques comme attributs privés — par exemple un <code>Circle</code> — et de les mettre
à jour dans les <em>setters</em>. Le reste se fait automatiquement. L'ajout de la
figure dans le composant se fait en l'ajoutant comme nœud enfant du composant (voir par exemple ce <a href="https://openclassrooms.com/courses/les-applications-web-avec-javafx/les-noeuds-graphiques">tutoriel</a> pour une explication sur l'organisation en
nœuds).</p>
<p>Notez au passage l'apparition d'une couleur <em>transparente</em>.</p>
<pre class="brush: java; gutter: false; highlight :16;">
// …
private Circle circle;
public GLed() {
circle = new Circle(50); // It's better to use a constant
circle.setFill(color.get());
circle.setStroke(Color.BLACK);
getChildren().add(circle);
}
public final void setOn(boolean aon){
on.set(aon);
if (aon) {
circle.setFill(color.get());
} else {
circle.setFill(Color.TRANSPARENT);
}
}
public final void setColor(Color c){
color.set(c);
circle.setFill(c);
}
</pre>
<p>Puisqu'avec Swing, je suis dans une optique où je précise quel type de <em>listeners</em>
j'utilise — en l'occurrence, des <em>PropertyChangeListener</em> — la classe qui veut
écouter une led doit implémenter <code>PropertyChangeListener</code> et écrire la
méthode <code>propertyChange()</code> <strong>ou</strong> bien utiliser les classes internes anonymes.
Par exemple,</p>
<pre class="brush: java; gutter: false;">
led.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(GLed.PROPERTY_ON)
&& !(Boolean) evt.getNewValue()) {
// do something
}
}
});
</pre>
<p>Avec JavaFX, le principe est semblable bien que plus général. Pour attraper un
événement (<em>event</em>) — tous les événements sont des <code>Event</code> — il faut
implémenter <code>ChangeListener<T></code> et écrire la méthode <code>changed</code> plus générique.</p>
<pre class="brush: java; gutter: false;">
led.onProperty().addListener(new ChangeListener< Boolean>() {
@Override
public void changed(ObservableValue< ? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
// do something
}
});
</pre>
<p>Dans cet exemple, le seul changement graphique lorsque la led s'allume,
s'éteint ou change de couleur, c'est la couleur de remplissage du cercle. Je
dois pourtant me charger de mettre à jour cette couleur dans chaque <em>setter</em>
par le biais de <code>circle.setFill(<color>)</code>. On me souffle dans l'oreillette
— merci — qu'il est possible de lier des propriétés entre elles. <em>bind</em>.</p>
<pre class="brush: java; gutter: false;">
circle.fillProperty().bind(color);
</pre>
<p>Avec ceci, dès que je change la propriété <em>color</em> via son <em>setter</em> par
exemple, la propriété <em>fill</em> de <em>circle</em> est également mise à jour. Et le
cercle change de couleur.</p>
<p>Ça ne suffira malheureusement pas dans notre cas.
Pour que le cercle change de couleur lorsque <em>color</em> change ou lorsque <em>on</em>
change… c'est un peu plus complexe. Il faut que les propriétés <em>color</em> et <em>on</em>
soient liées dans un <em>binding</em> spécifique qu'il faudra définir. Il hérite de
<code>ObjectBinding<T></code> où <code>T</code> sera de type <code>Color</code> dans notre cas puisque c'est
une couleur qu'il faut fournir à la propriété <em>fill</em>.</p>
<p>Avec une classe interne comme celle-ci</p>
<pre class="brush: java; gutter: false;">
private class FillShapeBinding extends ObjectBinding< Color> {
public FillShapeBinding() {
bind(color, on);
}
@Override
protected Color computeValue() {
return isOn() ? getColor() : Color.TRANSPARENT;
}
}
}
</pre>
<p>on pourra ajouter le <em>binding</em> à la propriété <em>fill</em> et les trois propriétés
seront liées.</p>
<pre class="brush: java; gutter: false;">
circle.fillProperty().bind(new FillShapeBinding());
</pre>
<p>Les <em>setters</em> sont maintenant de simples setters dans lesquels n'apparaissent
plus aucune référence à la classe <em>circle</em>.</p>
<h2>Et la gestion des évènements ?</h2>
<p>Je voudrais maintenant rendre ma led cliquable. Lorsque l'on clique dessus,
elle passe de l'état éteint (<em>off</em>) à l'état allumé (<em>on</em>) et <em>vice versa</em>.
Mon <em>JavaBean</em> doit donc écouter la souris. Il doit s'ajouter comme écouteur
des clicks (ou autres événements de la souris).</p>
<p>Avec Swing, la led doit alors implémenter <code>MouseListener</code> et écrire
toutes les méthodes de l'interface à savoir les cinq méthodes <code>MouseClicked</code>,
<code>MousePressed</code>, <code>MouseRelease</code>, <code>MouseEntered</code> et <code>MouseExited</code>. Même si
certaines méthodes peuvent avoir un corps vide, elles doivent être présentes.
Je ne peut pas hériter de <code>MouseAdapter</code> et ne réécrire que les méthodes qui
m'intéressent car j'ai déjà mangé mon héritage. Je pourrais utiliser une
classe interne anonyme dans mon constructeur mais dans ce cas je ne serais pas
« reconnu publiquement » comme étant écouteur de cette souris.</p>
<p>Bref, j'ajoute à mon code les instructions suivantes:</p>
<pre class="brush: java; gutter: false;">
public class GLed extends JPanel
implements Serializable, PropertyChangeListener, MouseListener {
// …
public GLed() {
// …
addMouseListener(this);
}
@Override
public void mouseClicked(MouseEvent e) {
setOn(!isOn());
}
@Override
public void mousePressed(MouseEvent e) {
// nothing to do
}
// some " no code" for mouseReleased(MouseEvent e),
// mouseEntered(MouseEvent e) and mouseExited(MouseEvent e)
}
</pre>
<p>Je peux toujours préférer la manière « classes internes ». De cette manière,
je peux utiliser la classe <code>MouseAdapter</code>. Ce qui me donne un code plus concis que je préfère:</p>
<pre class="brush: java; gutter: false;">
public class GLed extends JPanel
implements Serializable {
// …
public GLed() {
// …
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
setOn(!isOn());
}
});
}
}
</pre>
<p>À nouveau, JavaFX est plus générique et la led doit implémenter
<code>EventHandler<T></code> ou <code>T</code> sera ici <code>MouseEvent</code> ou utiliser une classe interne
anonyme. Lorsque l'on s'ajoute comme écouteur, il faut préciser quel évènement
précis on veut écouter. Dans cet exemple, c'est <code>MouseEvent.MOUSE_CLICKED</code>.</p>
<pre class="brush: java; gutter: false;">
public class GLed extends Parent {
// …
public GLed() {
// …
addEventHandler(MouseEvent.MOUSE_CLICKED,
new EventHandler< MouseEvent>() {
@Override
public void handle(MouseEvent event) {
setOn(!isOn());
}
});
}
}
</pre>
<p>Plus générique et plus concis !</p>
<p>Et si j'utilise les <a href="http://namok.be/blog/?post/2014/06/03/jdk8">lambdas</a> — ce que je n'ai sciemment pas fait ici
pour ne pas embrouiller le lecteur — je peux encore faire plus court. Il faudra trouver le bon équilibre entre concision et lisibilité mais ceci est une autre histoire.</p>
<pre class="brush: java; gutter: false;">
public class GLed extends Parent {
// …
public GLed() {
// …
addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> {
setOn(!isOn());
});
}
}
</pre>
<p>Vous pouvez reprendre une activité normale.</p>
<p><br/></p>
<p><em>Remarques. En octobre 2014, JavaFX sans OpenGl ne fonctionnait. Il n'était
pas possible de lancer une interface graphique. Pour résoudre le problème,
ajouter une option à la jvm: <code>-Dprism.order=sw</code>. Ça ne semble plus très utile
en octobre 2015. Pour ceux qui cherchent un tutoriel classique,
<a href="https://openclassrooms.com/courses/les-applications-web-avec-javafx/les-pre-requis-1">openclassrooms</a> en propose un.</em></p>
<p><em>Merci aux relecteurs; Anne, Cath, Sébastien et Jonathan</em></p>
<p><em>Crédit photo par <a href="https://www.flickr.com/photos/ptc24/14339698018/">Peter Corbett</a>; des leds d'une lampe de vélo. Conseil
sécurité. En vélo, si vous êtes vu, c'est principalement grâce à votre
<strong>vareuse</strong>. Pas parce que vous allumez trois malheureux leds.</em></p>
La force du certificat (auto-signé)urn:md5:7fab0e99983a1dff54cb731e15cdbdb22015-10-14T15:24:00+02:002015-10-14T14:32:28+02:00PiTMes doigts dans le clavierchamiloesigeekgooglemozillasecurity<p><img src="https://blog.namok.be/public/images/divers/2015/la-7-me-compagnie-1775688.jpg" alt="la-7-me-compagnie-1775688.jpg" style="margin: 0 auto; display: block;" title="la-7-me-compagnie-1775688.jpg, oct. 2015" /></p>
<p>— Le site est en http<strong>s</strong> donc c'est sécurisé.<br />
— Oui et non.</p>
<p>Un site en <em>https</em> signifie que la communication entre le serveur — le site
web auquel l'utilisateur accède — et le client — le navigateur internet — est
chiffrée. Si cette communication est chiffrée, elle ne pourra pas être lue par
un tiers. Le mot de passe de ton compte, le numéro de ta carte visa, la
recette de la tarte à mastelles, la photo de ta b… bagnole. Tout ça circule de
manière chiffrée en <code>https</code> ou en clair si c'est simplement en <code>http</code>.</p>
<p>Pour mettre en place une communication <em>tls</em> (anciennement <em>ssl</em>), il faut deux
ingrédients.</p>
<p>Le premier ingrédient permet effectivement que la communication entre les deux
parties soit chiffrée. C'est de <a href="https://fr.wikipedia.org/wiki/Cryptographie_asym%C3%A9trique">la cryptographie asymétrique</a> avec
génération de clés privées et publiques et partage de la clé publique. Dès lors
que la taille des clés et la méthode de chiffrement sont valables, la
communication ne sera pas déchiffrée (en un temps raisonnable et avec des
moyens raisonnables bla bla). Grâce à ces clés, on peut faire deux choses:</p>
<ul>
<li>vérifier l'identité d'un interlocuteur. En effet s'il me donne sa clé
publique et s'il prétend être qui il prétend, il détient alors la clé privée
et je peux lui envoyer un challenge;</li>
<li>établir un canal de communication sécurisé temporaire pour s'échanger une
clé de session qui chiffrera toute la suite de la communication. </li>
</ul>
<p>Le deuxième ingrédient se résume en une seule question :</p>
<blockquote>
<p><strong>« Mon interlocuteur est-il bien qui il prétend être ? »</strong>.</p>
</blockquote>
<p>Il est en effet possible qu'une personne malveillante substitue son site
web au site web auquel je voulais me connecter afin de dérober mes secrets
! C'est là qu'entre en jeu l'autorité de certification.</p>
<p>Le rôle de <strong>l'autorité de certification</strong> est simplement de garantir
l'identité de l'interlocuteur. Plus précisément la validité du couple identité
/ clé publique. Si je tente une connexion <em>https</em> au site <code>secure.example.org</code>
l'autorité de certification pourra me dire si c'est effectivement avec le
« bon » site <code>secure.example.org</code> que je communique. Si c'est le
cas, nous échangerons nos clés et nous communiquerons de manière chiffrée.
Sinon, mon navigateur me préviendra du danger.</p>
<p>Il existe différents types de certificats. La question du jour s'intéresse à la
confiance que l'on peut leur faire<sup id="fnref:ff"><a href="https://blog.namok.be/?post/2015/10/14/la-force-du-certificat#fn:ff" rel="footnote">1</a></sup>. On parle bien ici de la confiance que l'on
a en l'autorité qui me garantit qu'une autre personne est digne de confiance.
On pourrait s'intéresser à qui sont ces autorités mais ce n'est pas l'objet.</p>
<p>Le premier type de certificat que l'on rencontre est le <strong>certificat
auto-signé</strong>. À l'allure ci-dessous, je garantis que je suis bien qui je
prétends être.</p>
<p>— C'est qui ?<br />
— C'est moi.<br />
— Qui ça moi ?<br />
— Ben moi, ouvre !</p>
<pre><code>Émis pour
Nom commun (CN) secure.example.org
Organisation (O) ACME
Unité d'organisation (OU) ACME
Numéro de série AA:BB:11:22:CC:DD:33:44::EE
Émis par
Nom commun (CN) secure.example.org
Organisation (O) ACME
Unité d'organisation (OU) AME
Période de validité
…
</code></pre>
<p>En fonction du navigateur, le message d'alerte sera différent. <a href="https://www.google.com/chrome/browser/desktop/index.html">Google
Chrome</a> donnera toujours un avertissement qu'il sera « difficile
» d'ignorer tandis que <a href="https://www.mozilla.org/fr/firefox/new/">Mozilla Firefox</a> proposera d'ajouter une
exception permanente.</p>
<ul>
<li>Avec <a href="https://www.google.com/chrome/browser/desktop/index.html">Google Chrome</a> si le certificat change un jour, je n'en suis
pas informé car je dois ignorer l'avertissement à chaque connexion. Je suis
donc dans une « situation dangereuse » à chaque fois. Le choix de Google de
ne pas autoriser l'enregistrement d'une exception pousse à l'abandon des
certificats auto-signé. Est-ce un aspect sécuritaire ou commercial ? </li>
<li>Avec <a href="https://www.mozilla.org/fr/firefox/new/">Mozilla Firefox</a>, si je décide de faire confiance au site web
une première fois en ajoutant l'exception, il me préviendra si, soudain, le
certificat change. Je dois faire confiance lors de la première connexion. </li>
</ul>
<p>Un certificat auto-signé n'est pas <strong>nécessairement</strong> une mauvaise solution ni
une solution dangereuse. Je suis sûr que la connexion est chiffrée et je serai
informé — si j'utilise Mozilla Firefox et s'il ne le fait pas dès la première
connexion — si quelqu'un usurpe l'identité du site.</p>
<p>L'important, pour que la communication soit confidentielle, c'est d'être sûr
de l'identité de mon interlocuteur. C'est d'être sûr que la clé publique
présentée est bien la sienne. Un meilleur moyen que le certificat auto-signé
serait simplement l'affichage — avec surveillance — de l'empreinte de cette
clé sur la place publique. Le canal de communication de la clé doit être
sécurisé mais pas confidentiel.</p>
<p>Dans le cadre de l'école, les <strong>valves papiers</strong> font très bien l'affaire. Je
ne dois pas faire confiance à une société tierce et je suis certain que la clé
est la bonne puisque je le vérifie moi-même.</p>
<p>C'est bien dans le choix du type de certificat que se passe toute la
réflexion. Je n'ai pas besoin de la même sécurité lorsqu'il s'agit du site
d'une banque ou d'un site de commerce en ligne, d'un site de porn / rencontre,
d'un site de <em>elearning</em>…</p>
<p>Les <strong>autres types de certificats</strong> sont garantis par une autorité de certification
tierce et la « force » de la garantie dépend simplement du prix que l'on veut
bien payer ! En fonction de ce prix, l'autorité de certification fera des
vérifications de l'identité du demandeur plus ou moins poussées. Des
navigateurs comme Google Chrome et l'ignorance des utilisateurs m'imposent
d'utiliser un tel certificat pour éviter l'affichage d'un avertissement
à chaque connexion.</p>
<p>Pour les certificats les moins chers, l'autorité se contente de vérifier que
le demandeur a bien la main sur le domaine. L'une des trois méthodes
utilisée est:</p>
<ul>
<li>demande d'ajout d'un enregistrement DNS (il faut avoir la main sur la zone
concernée);</li>
<li>demande d'ajout d'une adresse mail particulière (il faut avoir la main sur
la zone ou sur le serveur de mail);</li>
<li>demande d'ajout d'un fichier texte particulier à la racine du domaine
à protéger (il faut avoir la main sur l'hébergement)</li>
</ul>
<p>Pour les certificats plus chers et donc plus surs, le demandeur devra montrer
des preuves de domiciliation, de propriété du nom de domaine… et l'autorité de
certification pourra offrir des garanties sur les transactions financières.</p>
<p>À partir de ±300€ / an, l'autorité de certification fournit la « <strong>green bar</strong>
» gage d'une vérification poussée.</p>
<p>On aura alors des certificats à l'allure suivante:</p>
<pre><code>Émis pour
Nom commun (CN) secure.example.org
Organisation (O) ACME
Unité d'organisation (OU) ACME
Numéro de série AA:BB:11:22:CC:DD:33:44::EE
Émis par
Nom commun (CN) VeriSign Class 3 Secure Server CA - G3
Organisation (O) VeriSign, Inc
Unité d'organisation (OU) VeriSign Trust Network
Période de validité
…
</code></pre>
<p>Le plus simple est de faire confiance à son navigateur. Le plus simple n'est
jamais le plus sécurisé. Lorsque le navigateur accepte un certificat TLS,
c'est bien. Je peux ne pas me poser de question. Lorsqu'il le refuse, rien
n'empêche de le regarder et de l'accepter en fonction du site web qu'il
chiffre.</p>
<p><br/></p>
<p><em>Crédit photo par Pitivier et ses amis chez <a href="http://www.live2times.com/1975-on-a-retrouve-la-7eme-compagnie-lamoureux-remet-ca-e--12872/">Live2times</a>. Qui donc que la
septième compagnie pour représenter l'autorité ?</em></p>
<p><em>Merci aux collègues qui ont accepté de relire.</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:ff">
<p>Faire confiance en une autorité de certification, c'est croire: 1/
qu'elle n'accorde pas de faux certificat à des tiers (voir <a href="http://rue89.nouvelobs.com/2011/03/18/tunisie-microsoft-complice-de-la-censure-numerique-par-ben-ali-195693">Microsoft en
tunisie</a>, l'affaire <a href="https://reflets.info/microsoft-et-ben-ali-wikileaks-confirme-les-soupcons-d-une-aide-pour-la-surveillance-des-citoyens-tunisiens/">Ben Ali</a>), 2/ qu'elle
vérifie correctement l'identité des demandeurs, 3/ qu'elle ne fait pas les
attaques <em>man in the middle</em> elle-même car elle est très bien placée pour le
faire. <a href="https://blog.namok.be/?post/2015/10/14/la-force-du-certificat#fnref:ff" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Javascript c'est (pas) comme Javaurn:md5:0c11bc485cb1f2843f45d716e29f96662015-10-10T12:27:00+02:002015-10-11T16:28:46+02:00PiTMes doigts dans le clavieresigeekjavajavascript<p><img src="https://blog.namok.be/public/images/divers/2015/blue_duck_by_sudlice-d7x15ds.jpg" alt="blue_duck_by_sudlice-d7x15ds.jpg" style="margin: 0 auto; display: block;" title="blue_duck_by_sudlice-d7x15ds.jpg, oct. 2015" /></p>
<p><em>ou « J'ai lu pour vous "<a href="http://shop.oreilly.com/product/0636920027065.do">Head first. Javascript Programming</a>" »</em></p>
<p><br/></p>
<hr />
<p><a href="https://blog.namok.be/?post/2015/10/10/javascript-versus-java#vocabulaire">Vocabulaire et concepts</a><br />
<a href="https://blog.namok.be/?post/2015/10/10/javascript-versus-java#type">Type</a><br />
<a href="https://blog.namok.be/?post/2015/10/10/javascript-versus-java#compilation-interpretation">Compilation, interprètation</a><br />
<a href="https://blog.namok.be/?post/2015/10/10/javascript-versus-java#closure">Closure et <em>first class value</em></a><br />
<a href="https://blog.namok.be/?post/2015/10/10/javascript-versus-java#inheritence">Héritage par prototype <em>versus</em> héritage classique (par classe)</a><br />
<a href="https://blog.namok.be/?post/2015/10/10/javascript-versus-java#falsey">falsex - trusthy</a></p>
<hr />
<p><br/></p>
<p>Javascript, c'est comme Java car il contient le mot <em>Java</em>.<br />
Javascript, c'est comme Java car une grande partie de la syntaxe est pareille:</p>
<ul>
<li>les accolades <code>{}</code> sont les délimiteurs de blocs;</li>
<li>les mots clés de base sont les mêmes (<code>do while for if else break continue
return switch throw try catch instance(o/O)f</code>…);</li>
<li>les littéraux numériques et logiques sont pareils;</li>
<li>les opérateurs arithmétiques, logiques et de comparaison — même
l'opérateur ternaire — existent dans les deux langages;</li>
<li>les commentaires se mettent aussi entre <code>/* */</code>;</li>
<li>les noms de variables contiennent des caractères, des chiffres et <code>$</code> et
<code>_</code>;</li>
<li>il est possible de définir des objets et tous héritent — attention, la
notion d'héritage est différente — de <code>Object</code>;</li>
<li>il existe des objets prédéfinis qui s'appellent; <code>Object</code>, <code>Date</code>, <code>String</code>,
<code>Math</code>…;</li>
</ul>
<p><br/></p>
<p><strong><em>It's all folks !</em></strong><br />
<strong>C'est tout les gars !</strong></p>
<p><br/></p>
<p>La similitude s'arrête là.<br />
Javascript ce n'est pas comme Java.</p>
<h2 id="vocabulaire">Vocabulaire, concepts et usage</h2>
<p>Java traite avec des classes, des interfaces, des objets, des attributs, des
méthodes (éventuellement) statiques tandis que Javascript manipule des objets,
des propriétés et des fonctions.</p>
<p>Certains concepts — s'ils ne sont pas semblables — sont (très) proches.
D'autres pas du tout. C'est à cette question que tente de répondre l'article
^^</p>
<p>L'usage que l'on veut faire du langage est très différent. Là où Java est un
langage orienté <em>desktop</em> et applications d'entreprise, Javascript est
interprèté par le navigateur et, de ce fait, il est très orienté applications
web. Il est très fort pour ça. Il s'intègre parfaitement avec HTML et CSS. Avec
<a href="https://nodejs.org/en/">Node.js</a> il se retrouve maintenant du côté serveur et des librairies
telles que <a href="http://nwjs.io">nw.js</a> le place également dans les applications desktop.</p>
<h2 id="type">Type</h2>
<p>Java est un langage fortement typé. Java demande que l'on déclare une variable
avant de l'utiliser. Le contenu de cette variable devra être (à conversion
près) du type annoncé. En Javascript, pas besoin de définir le type lors de
la « déclaration » d'une variable et, même si les expressions ont un type, une
variable peut recevoir n'importe quel type de valeur… et en changer au fur et
à mesure de l'exécution du programme.</p>
<p>Le mot clé <code>var</code> précisera le <em>scope</em> de la variable. Une variable pourra être
globale ou locale.</p>
<p>Une variable Javascript peut même recevoir une fonction comme valeur.</p>
<pre class="brush: js; gutter: false;">
var foo = function() {
// insert code
};
</pre>
<p>En Javascript, on a ce que l'on appelle, le <em>duck typing</em></p>
<blockquote>
<p>Si ça vole comme un canard, cancane comme un canard et nage comme un canard
alors, c'est un canard.<br />
<em>Javascript guy</em></p>
</blockquote>
<pre class="brush: js; gutter: false;">
var foo = "I'm a String"
// foo is a string
foo = new Duck()
// now, it's a duck
</pre>
<p>En Java, une variable a un certain type et le type de l'instance doit être le
même ou être un enfant.</p>
<blockquote>
<p>Si c'est un canard alors… c'est un canard.<br />
<em>Java guy</em></p>
</blockquote>
<pre class="brush: java; gutter: false;">
Duck duck = new Duck();
// duck is a duck
Bird bird = new Duck();
// bird is Bird type and Duck type instance
// Duck must inherit from Bird
</pre>
<h2 id="compilation-interpretation">Compilation, interprètation</h2>
<p>Java est un langage <strong>compilé et interprèté</strong>. Le compilateur — qui
génère le <em>bytecode</em> exécuté par la machine virtuelle Java — vérifiera la
concordance des types lors de son analyse sémantique. Au <em>runtime</em> une
seconde vérification aura lieu afin de voir si le type de l'instance est bien
du type attendu.</p>
<p>Javascript est un langage interprèté par le navigateur. L'interprètation se
fait en deux passes. Lors de la première passe, le navigateur interprète les
déclarations de fonctions et les « mémorise ». Dans un second temps, il
interprète tout le reste.</p>
<p>Il est possible en Javascript de déclarer des fonctions de deux manières
différentes.</p>
<p>La méthode classique — au sens, semblable aux autres langages:</p>
<pre class="brush: js; gutter: false;">
function foo() {
// insert code
}
</pre>
<p>La méthode « déclaration de variable » puisqu'une variable peut être une
fonction:</p>
<pre class="brush: js; gutter: false;">
var foo = function() {
// insert code
};
</pre>
<p>La seule différence entre ces deux notations est le moment où elles sont
évaluées. Dans le premier cas, l'évaluation est faite lors de la première
passe et dans le second cas, lors de la deuxième passe. Cela peut, bien
sûr, avoir une influence sur le code.</p>
<p><strong>Remarque</strong> Pour tester facilement du code Javascript, installez <a href="http://getfirebug.com/">firebug</a></p>
<h2 id="closure">Closure et <em>first class value</em></h2>
<p>Les fonctions Javascript — à l'inverse des méthodes (statiques) Java — sont
des <em>first class values</em> (valeurs de première classe, traduction libre). Une
<em>first class value</em>:</p>
<ul>
<li>peut être assignée à une variable (ou un tableau, une <em>map</em>);</li>
<li>peut être passée en paramètre d'une fonction;</li>
<li>peut être retournée par une fonction</li>
</ul>
<p>Javascript permet donc d'écrire:</p>
<pre class="brush: js; gutter: false;">
function addN(n){
var adder = function(x){
return x+n;
}
return adder;
}
var add2 = addN(2);
var result = add2(3), // result equals 5
</pre>
<p>Allons un pas plus loin comme le propose <a href="http://shop.oreilly.com/product/0636920027065.do">Head First Javascript</a> et
écrivons: </p>
<pre class="brush: js; gutter: false;">
var foo = "Global";
function bar(){
var foo = "Local";
function inner(){
return foo,
}
return inner; // return the function
}
var baz = bar();
var result = baz();
// result equals Local or Global ?
</pre>
<p>Puisque l'on retourne une fonction, l'appel à <code>baz</code> est un appel de la
fonction <code>inner</code>… puisque <code>bar</code> retourne cette fonction <code>inner</code>.</p>
<p>La question de savoir que retourne la fonction <code>baz</code> est la même que de se
demander si Javascript retourne la variable locale ou la variable globale. Mon
raisonnement — si je suis habitué à des langages comme Java et C++ et que je
suis l'appel des méthodes, la création / destruction des variables locales
— me dit que c'est <code>"Global"</code> qui est retourné. En effet, je pense — à tort
comme nous allons voir — que comme la fonction <code>bar</code> n'est appelée qu'une
seule fois lors de l'assignation de <code>bar</code> à <code>baz</code>, la variable locale <code>foo</code>
n'existe plus lors de l'appel à <code>baz</code>.</p>
<p>— C'est sans compter sur les closures, mon bon.<br />
— Qu'est-ce donc, mon brave ?</p>
<p>Une <strong>closure</strong> est une fonction <strong>et</strong> l'environnement dans lequel elle est
définie. Dans l'exemple, lorsque <code>bar</code> retourne <code>inner</code>, elle retourne aussi
l'environnement de <code>bar</code>… à savoir la variable locale <code>foo = "Local"</code>. Dès
lors, quand <code>baz</code> est appelé, <code>inner</code> est exécutée dans l'environnement dans
lequel elle a été définie. Elle retourne donc la valeur <code>"Local"</code>.</p>
<blockquote>
<p><strong>closure</strong> n.f. Une fonction et son environnement c'est-à-dire le contexte
dans lequel elle a été définie.</p>
</blockquote>
<p>Les fonctions Javascript sont donc toujours évaluées dans l'environnement dans
lequel elles ont été définies. Leur <em>lexical scope</em>, portée lexicale. Grâce
à ce concept, on pourra éviter l'utilisation de variables globales. <em>Les
variables globales, c'est le mal</em>.</p>
<p>Là où je pourrais avoir envie d'écrire (mais c'est mal, rappelle-toi):</p>
<pre class="brush: js; gutter: false;">
var count = 0;
function counter() {
count++;
return count;
}
// counter(); counter()…
</pre>
<p>avec les <em>closures</em>, je peux me passer de la variable globale en ajoutant « un
niveau de fonction » comme suit:</p>
<pre class="brush: js; gutter: false;">
function makeCounter() {
var count = 0;
function counter() {
count++;
return count;
}
return counter;
}
var do = makeCounter();
// do(); do(); do()
</pre>
<p>La variable <code>count</code> est une <strong>variable libre (<em>free variable</em>)</strong> dans la
fonction <code>counter</code>, elle n'est pas globale, ni locale ni un paramètre. La
closure est faite par la fonction <code>makeCounter</code> qui retourne la fonction
<code>counter</code> et son environnement. Environnement qui contient la variable
<code>count</code>. Même après l'appel (unique) à la méthode <code>makeCounter</code>, la variable
(locale à <code>makeCounter</code>) continue d'exister dans l'environnement de la
fonction <code>counter</code>.</p>
<p>Nous verrons plus encore l'utilité de ceci lorsque l'on voudra programmer en
orienté objet — et donc mettre en œuvre l'héritage et le polymorphisme — et
que l'on constatera que Javascript n'implémente pas la notion classique par
classes de l'orienté objet mais un héritage par prototype.</p>
<h2 id="inheritence">Héritage par prototype <em>versus</em> héritage classique (par classes)</h2>
<p>Javascript permet de créer des <em>objects literals</em> (des littéraux objets) qui
représentent une instance d'un objet. Un objet a des propriétés et des
fonctions.</p>
<pre class="brush: js; gutter: false;">
var duck = {
name: "Ducky",
// …
foo: function() {
// code function
}
}
</pre>
<p><strong>Comment faire pour déclarer une classe et instancier moult objets de la même
classe ?</strong></p>
<p>Java permet de déclarer une classe. Cette classe contient des membres
— attributs, méthodes et constructeurs — statiques ou non. Les membres
statiques sont partagés par toutes les instances. Les autres sont propres
à une instance.</p>
<pre class="brush: java; gutter: false;">
public class Duck{
protected String name;
public Duck(String n){
this.name = n;
}
public void foo() {
// code method
}
}
Duck duck = new Duck("Ducky");
</pre>
<p>Javascript permet de créer un constructeur et d'instancier des objets à partir
de ce constructeur et du mot clé <code>this</code>. Un constructeur est une fonction
contenant des propriétés — précédées de <code>this</code> qui référence l'objet — et des
fonctions. Par convention ce constructeur s'écrit en <em>CamelCase</em> — le nom
commence par une majuscule — comme en Java.</p>
<pre class="brush: js; gutter: false;">
function Duck (name){
this.name = name;
// …
this.foo = function() {
// code function
};
}
var duck = new Duck("Ducky");
</pre>
<p>Javascript va créer toutes les propriétés définies dans le constructeur
<strong>y compris les fonctions</strong> pour toutes les instances. Une fonction sera donc
présente — avec son environnement — autant de fois que l'objet est instancié.
C'est un problème.</p>
<p>La solution est le <strong>prototype</strong>. Ce prototype est une propriété du
constructeur. Dans ce prototype (unique) peuvent se trouver des propriétés et
des fonctions. Ces propriétés et fonctions sont communes à toutes les
instances créées à partir du constructeur.</p>
<p>Si l'on ajoute des propriétés ou des fonctions au prototype alors que des
objets ont déjà été créés, pas de soucis. Toutes les instances présentes et
à venir en bénéficieront. Si l'on décide de réécrire une fonction dans une
instance particulière seule cette instance bénéficiera de la réécriture.
Javascript cherche une propriété ou une fonction dans l'objet. S'il ne trouve
pas, il cherche dans le prototype. On écrira donc quelque chose à l'allure
suivante:</p>
<pre class="brush: js; gutter: false;">
function Duck(name){
this.name = name;
// …
}
Duck.prototype.foo = function() {
// code function
}
</pre>
<p>Si une propriété définie dans le prototype — et donc disponible et partagée
par toutes les instances — est changée par une instance alors une copie de
cette propriété est attribuée à l'instance. La valeur initiale, celle dans le
prototype est conservée.</p>
<pre class="brush: js; gutter: false;">Duck.prototype.bar = false;
Duck.prototype.baz = function() {
this.bar;
// bar = false, it's prototype value
this.bar = true;
// new bar property for this
}
var duck = new Duck("Ducky");
duck.baz()
// duck have now new property bar. value is true
</pre>
<p>Il est possible d'avoir une chaine de prototypes. C'est « l'héritage par
prototype ». Là où Java — via le mot clé <em>extends</em> — permet de créer une
nouvelle classe, Javascript — et son héritage par prototype — demande de
préciser que le prototype est celui du parent. Pour écrire correctement un
objet enfant en Javascript, on peut écrire:</p>
<pre class="brush: js; gutter: false;">
function Child() {
Parent.call(this, …);
// some code
}
Child.prototype = new Parent();
Child.prototype.constructeur = Child;
Child.prototype.foo = …
</pre>
<p>Le constructeur de l'enfant appelle le constructeur du parent. Le prototype de
l'enfant est celui du parent. C'est ce qui marque l'héritage. Le constructeur
de l'enfant s'appelle <em>Child</em>. Sans ça, il s'appellerait <em>Parent</em>. Le
prototype de l'enfant est complété. Il est possible de réécrire des fonctions
définies dans le parent.</p>
<p>Comme en Java toutes les classes héritent de la classe <code>Object</code>, en Javascript
tous les constructeurs héritent du prototype de <code>Object</code>. Cet objet propose
quelques fonctions semblables aux méthodes de la classe <code>Object</code> de Java:
<code>toString</code>, <code>hasOwnProperty</code>, <code>valueOf</code>…</p>
<p>En Java, si je veux « enrichir » un objet, j'en hérite ou je réédite la classe
et recompile. Pour les classes du langage, je ne peux pas les compléter. En
Javascript, je peux directement compléter le prototype de n'importe quel
objet. À utiliser avec parcimonie.</p>
<pre class="brush: js; gutter: false;">
String.prototype.foo = function() {…}
</pre>
<h2 id="falsey"><em>falsey - trusthy</em></h2>
<p>Java n'accepte que deux valeurs booléennes; <code>true</code> et <code>false</code>. Lors de
l'évaluation d'une expression booléenne, celle-ci doit donc être vraie ou
fausse. Parfois, on aimerait dire que c'est faux alors que la valeur n'est pas
(vraiment) fausse. On voudrait dire que si c'est <code>null</code> c'est faux ou bien que
si c'est <code>undefined</code>, c'est faux…</p>
<p>Javascript propose la <em>falsey value</em> qui peut apparaitre dans un test.</p>
<blockquote>
<p><em>falsey value</em>: <code>undefined</code>, <code>0</code>, <code>NaN</code>, <code>null</code>, <code>""</code></p>
</blockquote>
<p>Toute valeur qui n'est pas <em>falsey</em> est <em>trusthy</em>.</p>
<p><br/></p>
<p>Javascript et Java sont deux langages différents, plus personne n'en doute
maintenant. Cet article ne pointe que l'une ou l'autre différences <strong>propres au noyau du langage</strong>. Je ne m'intéresse pas ici aux différences dans la gestion des évènements, des entrées / sorties, des formats de communications entre applications…</p>
<p>Il reste énormément à dire sur Java et sur Javascript. Le but n'était pas de présenter les deux langages. Pour aller plus loin, je vous encourage à lire <a href="http://shop.oreilly.com/product/0636920027065.do">Head First Javascript Programming</a>. Pour avoir une introduction d'un autre genre, <a href="http://connect.ed-diamond.com/Linux-Pratique/LPHS-024/">Linux Pratique Hors Série 24</a> nous parle de Javascript, <a href="https://jquery.com">JQuery</a> et <a href="https://angularjs.org/">AngularJS</a>. <a href="http://phantomjs.org/">PhantomJS</a> nous permet d'<a href="http://namok.be/blog/?post/2015/07/29/recuperer-derniere-image-instagram">exécuter du code JS sans navigateur</a>.</p>
<p>Vous pouvez reprendre une activité normale…</p>
<p><br/></p>
<p><br/></p>
<p><em>Crédit photo chez <a href="http://deviantart.com">DeviantArt</a> par <a href="http://sudlice.deviantart.com/art/Blue-Duck-478744192">Sudlice</a>. Si ça vole
comme un canard, cancane comme un canard et nage comme un canard. Alors, c'est
un canard… bleu.</em></p>
LaTeX, markdown et wallpaperurn:md5:a3aa792e7c5cf3dd5e1102a9a666848a2015-10-08T16:57:00+02:002016-01-20T14:04:46+01:00PiTMes doigts dans le clavierdiversesigeekinutilelatexmarkdown<p><img src="https://blog.namok.be/public/images/divers/2015/screenshot-latex-wallpaper-12.jpg" alt="screenshot-latex-wallpaper-12.jpg" style="margin: 0 auto; display: block;" title="screenshot-latex-wallpaper-12.jpg, oct. 2015" /></p>
<p>Si toi aussi tu rédiges en <a href="http://namok.be/blog/?post/2013/11/19/billet-markdown">markdown</a> que tu exportes en pdf et que tu aimes bien utiliser des images de grandes tailles, alors le <em>package</em> <strong>Wallpaper</strong> (<a href="https://www.ctan.org/pkg/wallpaper?lang=en">doc</a>) est fait pour toi. Je suppose aussi que tu exportes en utilisant <a href="http://namok.be/blog/?post/2014/04/01/vrac-14">pandoc</a> avec une commande du style:</p>
<p><code>pandoc monjolifichier.md -o monjolifichier.pdf</code></p>
<p>Tu peux commencer ton document avec ce mélange de markdown et de LaTeX un peu pourri mais qui fait le boulot:</p>
<pre><code> ---
title: \Huge Awesome title
author: \huge Juste Leblanc
date: \LARGE\tt jl@example.org
abstract: \bigskip\centering\huge\bf\textcolor{white}{octobre 2015 \\ v1.0}
header-includes:
- \usepackage{wallpaper}
- \renewcommand{\abstractname}{}
geometry: margin=3cm
---
\ThisTileWallPaper{\paperwidth}{\paperheight}{img/awesomeimage.jpg}
(ou bien)
\ThisLLCornerWallPaper{1}{img/awesomeimage.jpg}
</code></pre>
<p>Et voilà, ce billet me sert d'aide mémoire.</p>