LongPathsEnabled pour utiliser pip (et installer ansible)

30 August 2021

Pour installer ansible, j'utilise la ligne de commande dans un terminal windows.

pip install ansible

L'installation échoue parce que les fichiers detarés ne sont pas accessibles. Il s'agit apparement d'un problème lié à la longueur du path des fichiers.

Il semblerait qu'il soit possible de changer le comportement de windows à partir de la release 1607 de windows 10.

https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later

En effet la valeur de

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem]
"DisableDeleteNotification"=dword:00000000
"FilterSupportedFeaturesMode"=dword:00000000
"LongPathsEnabled"=dword:00000000
"NtfsAllowExtendedCharacter8dot3Rename"=dword:00000000
"NtfsBugcheckOnCorrupt"=dword:00000000
"NtfsDisable8dot3NameCreation"=dword:00000002
"NtfsDisableCompression"=dword:00000000
"NtfsDisableEncryption"=dword:00000000
"NtfsDisableLfsDowngrade"=dword:00000000
"NtfsDisableVolsnapHints"=dword:00000000
"NtfsEncryptPagingFile"=dword:00000000
"NtfsMemoryUsage"=dword:00000000
"NtfsMftZoneReservation"=dword:00000000
"NtfsQuotaNotifyRate"=dword:00000e10
"RefsDisableLastAccessUpdate"=dword:00000001
"ScrubMode"=dword:00000002
"SymlinkLocalToLocalEvaluation"=dword:00000001
"SymlinkLocalToRemoteEvaluation"=dword:00000001
"SymlinkRemoteToLocalEvaluation"=dword:00000000
"SymlinkRemoteToRemoteEvaluation"=dword:00000000
"UdfsCloseSessionOnEject"=dword:00000003
"UdfsSoftwareDefectManagement"=dword:00000000
"Win31FileSystem"=dword:00000000
"Win95TruncatedExtensions"=dword:00000001
"NtfsDisableLastAccessUpdate"=dword:80000002
"SuppressInheritanceSupport"=dword:00000001

Il faut passer la valeur de "LongPathsEnabled" à 1.

Ce changement a un effet sur l'installation dans la mesure ou l'on peut avancer un cran plus loin.
Il est possible que d'autres bénéfices apparaissent dans d'autres applications, surtout celles "unix-like".

Note : de toute façon, sans environnement unix, ansible n'est pas très fonctionnel... donc j'ai refait toute l'installation dans WSL.

Réflexion projets perso

13 August 2021

L'objectif de ce billet de blog est de faire un petit bilan de projets personnels, et d'identifier des pistes pour la suite.

Domotique

Ce qui est en place

Des prises commandées et des commutateurs Sonoff ont été tasmotizées mises en place. Des sondes basées sur des Lolin V1 Lite avec shield DHT-22, également sous tasmota ont été déployées dans une chambre, au salon, au garage.

Domoticz et Mosquitto server (un serveur MQTT) ont été installés sur un Raspberry 3. La communication vers les capteurs et les switchs se fait exclusivement via des messages MQTT pour le moment.

Dans domoticz, des essais d'automatisation ont été réalisés, tout d'abord avec le langage visuel "Blocky", (un langage visuel "scratch-like" embarqué dans domoticz), puis avec le module de scripting en Lua avec une API évenementielle ad hoc (dzEvents). Un asservissement à été mis en place avec la température de la pièce, pour allumer le chauffage en desssous d'une certaine température et l'éteindre au dela.

A noter la confection d'un capteur de température qui a également la forme d'un Lolin V1 Lite (une board ESP-8266) non basé sur Tasmota, mais programmé à partir de Arduino IDE, et qui gère un affichage des températures sur un carré de ePaper de 2,54 pouces. La programmation pourrait être améliorée en endormant / rallumant le module. La mise en someil et le réveil on lieu comme il faut dans les tentatives de prototypage, mais le programme ne se relance pas proprement.

Le tout a été plaçé sur un réseau à part, qui est routé et firewallé depuis un Linksys WRT-54 GS avec OpenWRT.

Les sondes sont visibles depuis le réseau domestique (modulo le fait de configurer un routage vers le réseau des capteurs en ajoutant l'adresse du WRT 54 GS comme passerelle), mais le réseau domestique n'est pas visible depuis elles. Le RPi a un accès à internet, sans que le résau domestique ne soit visible non plus depuis le réseau capteur.

Pour aller plus loin

J'avais prévu le titre "Ce qui reste à faire", mais c'est forcément illimité...

Plus de capteurs

Il manque des capteurs et des actionneurs pour que les choses soient plus amusantes.

A prototyper par exemple, l'utilisation d'un Sonoff avec deux voies, pour piloter un volet roulant. L'appareil a été commandé, mais pas encore prototypé.

Pour aller plus loin, il faudrait remplacer les interupteurs de la maison par des switch sonoff (ou autre référence), afin de pouvoir capter l'état et le commander depuis domoticz.

Une problématique simple : comment changer les interrupteurs et conserver une utilisation "manuelle" efficace, d'une part, et conserver le "look" des autres boitiers interupteurs (commutateurs Schneider Ovalis, pour être précis) ? Une solution serait de brancher des poussoirs Ovalis avec une plaque Sonoff ou similaire à l'intérieur, quitte à dessiner une boite de saillie plus adaptée en 3D.

Il manque encore des capteurs divers pour rendre les choses amusantes :

  • ouvertures de portes
  • capteurs de gaz / fumées
  • détecteur d'eau
  • Capteurs de présence
  • Caméra / reconnaissance de forme ?

Les têtes de radiateur sont aussi une piste à explorer, avec une partie capteur et une partie actionneur.

Sinon, les autres actionneurs sont des moteurs, valve d'eau, etc.

Enfin, en basculant plutot sur home-assistant comme logiciel d'intégration, une intégration audiovisuelle semble plus simple à mettre en place.

Pour la commande d'éclairage, la commande de bandeau de LED est à regarder. Il s'agit aussi de pouvoir disposer d'un eclairage de qualité avec un IRC correct.

Plus de fiabilité

Il reste que les sondes de température demandent à être fiabilisées. Elles se bloquent régulièrement. Peut-être le pbm vient-il du reset ?

Faciliter la maintenance

Commençons par l'achat : je ne peux pas trouver le matériel Sonoff dans les magasins du coin. Les prises connectées vendues au Leroy-Merlin ne sont malheureusement pas basée sur des ESP-32, et ne peuvent pas être tasmotizées.

Par ailleurs, faut-il chercher une compatibilité avec la plateforme Tuya ?

Un des intérets de l'utilisation de Tasmota, est d'être complètement local, et d'avoir une (assez bonne) confiance dans le fait que mes données ne sont pas utilisées par les Gafam.

Autres pistes

Monitorer le NAS en utilisant Cacti ?

Bricolage

Je ne vais pas lister tous les sujets de bricolage dans la maison, mais pourtant il y en a.

Listons plutot les ressources de bricolage :

  • Matériel électronique : composants, oscilloscope, poste à souder. Il manque un générateur de signaux.
  • Pour le travail miniature, mon ancienne perceuse et son support doivent encore pouvoir rendre des services.
  • Etabli, et outils electroportatifs :
    • perceuse
    • meuleuse
    • scie sauteuse
    • scie circulaire.

Il manque à l'évidence une solution d'impression 3D. J'hésite entre plusieurs options, soit de fabriquer entièrement l'imprimante, soit en kit, ou bien se tourner vers une marque qualitative. Si j'opte pour une Prusa, les deux derniers points peuvent être atteints.

Une autre option serait de construire plutot une machine CNC.

Par ailleurs, le pilotage par du g-code pourrait également donner lieu à d'autres projets. Pourquoi pas une marionnette à fil dont le mouvement serait donnée par du G-Code ?

Une des problèmes est la manutention du matériel. Sans voitre, il est difficile d'aller chercher des planches de bois, ou des outils lourds.

Aménagement

L'aménagement des chambres et de la salle de bain est satisfaisant. Il reste à améliorer la pièce à vivre, la cuisine, et surtout le salon.

Plusieurs envies pour ma part au salon :

  • pouvoir écouter de la musique
  • pouvoir regarder des films dans de bonnes conditions
  • pouvoir faire du montage / mixage / enregistrement / fond vert ?
  • pouvoir bricoler avec outils éléctroniques

Pour ce qui me concerne, l'envie serait de faire des vidéos pour illustrer la crétion de projets, ce qui imposerait de planifier la création d'un objet, de capturer les momens essentiels de la fabrication en vidéo et photo et de monter le tout.

Production audio-visuelle

Pour le moment, mes modestes moyens sont d'utliser le PC Dell antidéluvien et de composer les vidéos avec kdenlive, ce qui marche pour faire des choses simples comme d'assembler des prises de vues de matchs et ajouter quelques titres.

Use certificates for javamail

03 May 2021

Lorsque l'on utiliser javamail pour acceder à un dossier imap distant, il faut activer la sécurité d'une manière ou d'une autre.

Lorsque le serveur distant n'est pas identifié, la connexion échoue. L'approche est donc d'ajouter le certificat dans le keystore de la machine.

Mais pour cela, il faut récupérer le certificat imap...

Si l'on en croit l'exemple suivant :

openssl s_client -crlf -connect imap.gmail.com:993

Essayons avec outlook :

 openssl s_client -crlf -connect outlook.office.com:993
CONNECTED(00000003)
depth=1 OU = generated by Avast Antivirus for SSL/TLS scanning, O = Avast Web/Mail Shield, CN = Avast Web/Mail Shield Root
verify error:num=19:self signed certificate in certificate chain
verify return:1
depth=1 OU = generated by Avast Antivirus for SSL/TLS scanning, O = Avast Web/Mail Shield, CN = Avast Web/Mail Shield Root
verify return:1
depth=0 C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, CN = outlook.com
verify return:1
---
Certificate chain
 0 s:C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, CN = outlook.com
   i:OU = generated by Avast Antivirus for SSL/TLS scanning, O = Avast Web/Mail Shield, CN = Avast Web/Mail Shield Root
 1 s:OU = generated by Avast Antivirus for SSL/TLS scanning, O = Avast Web/Mail Shield, CN = Avast Web/Mail Shield Root
   i:OU = generated by Avast Antivirus for SSL/TLS scanning, O = Avast Web/Mail Shield, CN = Avast Web/Mail Shield Root
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIGDTCCBPWgAwIBAgIQf2v1kM3g30qn0VeY2wBgMjANBgkqhkiG9w0BAQsFADCB
gTE6MDgGA1UECwwxZ2VuZXJhdGVkIGJ5IEF2YXN0IEFudGl2aXJ1cyBmb3IgU1NM
L1RMUyBzY2FubmluZzEeMBwGA1UECgwVQXZhc3QgV2ViL01haWwgU2hpZWxkMSMw
IQYDVQQDDBpBdmFzdCBXZWIvTWFpbCBTaGllbGQgUm9vdDAeFw0yMTAxMjIwMDAw
MDBaFw0yMjAxMjEyMzU5NTlaMGoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
cG9yYXRpb24xFDASBgNVBAMTC291dGxvb2suY29tMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAmqIKpn8LB0Ua8T0vvcQsjtNrnMeu2OVHIr/zmeUzX3Lg
B2+PJ1xW15XQVo/hbePRx8MJTgf8RZyNWpPE+449aINjPWWH6qwRugvEmk5l0e1B
KxGUOVf+2IelHvTHuehrQVkooHiea3yxraBmwTv6VTBflw0teiLJeTmOd+yEgS0i
4XN4EMfsMm7xfXbRtjjO51yjpA0+yNnNRVABa1dDSqP5KpQMA7MAOEq6CpHB9Q0V
9qWLleJ7eNalEi9xtL4qYymtu5Uxw4hZnK7vBbKnJ82+3luympijt1a1J7G9xkhP
0pdJ7/zVWsFjDDbC4v97gYRQcMrHBxXqT6cLkLRrbwIDAQABo4IClTCCApEwHwYD
VR0jBBgwFoAUt09vaMwWcAlSJiKYlingDONaai8wHQYDVR0OBBYEFMLvNs1nc3bR
D1Upvc7JxzoO5Y85MIICEAYDVR0RBIICBzCCAgOCFiouY2xvLmZvb3RwcmludGRu
cy5jb22CDSouaG90bWFpbC5jb22CFiouaW50ZXJuYWwub3V0bG9vay5jb22CCiou
bGl2ZS5jb22CFioubnJiLmZvb3RwcmludGRucy5jb22CDCoub2ZmaWNlLmNvbYIP
Ki5vZmZpY2UzNjUuY29tgg0qLm91dGxvb2suY29tghcqLm91dGxvb2sub2ZmaWNl
MzY1LmNvbYIbYXR0YWNobWVudC5vdXRsb29rLmxpdmUubmV0gh1hdHRhY2htZW50
Lm91dGxvb2sub2ZmaWNlLm5ldIIgYXR0YWNobWVudC5vdXRsb29rLm9mZmljZXBw
ZS5uZXSCFmF0dGFjaG1lbnRzLm9mZmljZS5uZXSCGmF0dGFjaG1lbnRzLXNkZi5v
ZmZpY2UubmV0gh1jY3MubG9naW4ubWljcm9zb2Z0b25saW5lLmNvbYIhY2NzLXNk
Zi5sb2dpbi5taWNyb3NvZnRvbmxpbmUuY29tggtob3RtYWlsLmNvbYIWbWFpbC5z
ZXJ2aWNlcy5saXZlLmNvbYINb2ZmaWNlMzY1LmNvbYILb3V0bG9vay5jb22CEm91
dGxvb2sub2ZmaWNlLmNvbYIUc3Vic3RyYXRlLm9mZmljZS5jb22CGHN1YnN0cmF0
ZS1zZGYub2ZmaWNlLmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEB
AJtb5K4fD3BipH4w0gxVHnR/RxLNpEEUlhjizsKaBm0uUzc10/lbcSAq6j5J8Dfy
IyUBPCrJN2f51+mL5Aw5YkHjk/mCQS8ShzTuCWPaInn3zZJrL98KiOYcKPwSdBI8
NJN7+O1H75t/oENT1UOK4H/MFQ5DCFe+sk56fw+euAaGjyqM93pMF+k4NcSvBOMG
n2P32OLDyGICoCTuszN6DIUPPYtdywQprdu7RS9+URYxmkVp9PQK7BQnGTv/KAMr
bKfwrFHRex5AvqptawNvOYAw9aeva5hbWZBzdiQGsKkvCqlVQaV057iy80CfSM7M
SE6ytc9O4Sv+7V2nNO170G0=
-----END CERTIFICATE-----
subject=C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, CN = outlook.com

issuer=OU = generated by Avast Antivirus for SSL/TLS scanning, O = Avast Web/Mail Shield, CN = Avast Web/Mail Shield Root

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 3209 bytes and written 403 bytes
Verification error: self signed certificate in certificate chain
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: 88DC133498F921EA0F3F9D79A946273A720DEE347C0DF7605F00B03DD085A9E3
    Session-ID-ctx: 
    Master-Key: 68AEE9B356E250BF04BCA2175F4731D2B36796AAB76617B7BAC884B6C39ADAA345A873D1A1A93762EE4629EAD391F022
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 7200 (seconds)
    TLS session ticket:
    0000 - b9 b6 03 65 96 07 f0 aa-05 f2 c0 33 dc 82 c8 aa   ...e.......3....
    0010 - 2b 7d be 6e 84 9d e4 1e-d7 49 68 cd 52 3a 3d 4c   +}.n.....Ih.R:=L
    0020 - d1 55 60 7e f2 fc fc 83-59 fa 76 6c 31 7b fb e6   .U`~....Y.vl1{..
    0030 - bb c1 88 43 a2 29 2d 8b-b7 68 22 84 5d cc 1a 74   ...C.)-..h".]..t
    0040 - b2 32 2c 0b 25 ab 32 82-ea b6 58 45 c2 52 e9 19   .2,.%.2...XE.R..
    0050 - 38 25 24 30 9d 97 cb 1b-b0 a3 b1 09 a7 c4 cc 74   8%$0...........t
    0060 - d6 18 43 a3 0e d6 b3 a6-01 5a ed ec 5f 36 5d 1f   ..C......Z.._6].
    0070 - e1 b5 cd f7 1c 47 62 03-ea 9f 8e 6d 1a fc c5 cd   .....Gb....m....
    0080 - 53 53 31 7c 9d c2 bf bc-7f 2e aa dc e6 83 a7 95   SS1|............
    0090 - 2e a7 bf f0 21 ac fc 8f-ef 29 91 ca 33 d7 91 61   ....!....)..3..a

    Start Time: 1620054924
    Timeout   : 7200 (sec)
    Verify return code: 19 (self signed certificate in certificate chain)
    Extended master secret: yes
---
* OK The Microsoft Exchange IMAP4 service is ready. [UABSADMAUAAxADkAMwBDAEEAMAAwADMANgAuAEUAVQBSAFAAMQA5ADMALgBQAFIATwBEAC4ATwBVAFQATABPAE8ASwAuAEMATwBNAA==]

Ici on remarque que la sécurité est interceptées par l'antivirus Avast, qui substitue son propre certificat.

Pour l'exemple d'outlook.office.com, il faut noter que la requete fonctionne sur le port 993, mais pas sur le port 587.

Ajout dans le keystore

La réponse SO suivante donne une solution

echo | openssl s_client -connect yoursever:port 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > yourcert.pem
keytool -keystore cacerts -importcert -alias youralias -file yourcert.pem

C'est manifestement celle reprise par MA dans le README.md

Ensuite, soit l'on créé un fichier local, et il faut le faire connaitre au programme java, soit on utilise le keystore de la machine.

Attention, le fichier keystore n'existe pas forcement.

Junit4 a Junit5 ... et Spring

21 April 2021

Migration de Junit4 à Junit 5

Il faut changer les imports, et les @Before en @BeforeEach, et les @BeforeClass en @BeforeAll.

Outre ce changement, les runners n'existent plus.

Pour continuer à charger le contexte Spring dans le test, il faut ajouter
import

org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)

JunitParams et @Parameterized

JUnitParams n'existe plus.

Il faut réimplémenter la méthode appellée en l'annotant "@Parameterized", et qui peu directement utiliser un csv.

TODO example

[Finalement, j'opte pour un test dynamique, mais uniquement parce que le path du fichier n'est pas connu à la compilation].

Modules java et outils

12 April 2021

Problématique

L'idée pincipale est de pouvoir avoir des tests configurable par fichier de configuration, et executable par maven dans un environnement container.

Apercu des tests

Les tests dont je parle ici son au départ des tests JUnit4. L'application n'utilisait pas Spring Boot au départ.

Dans mes tests de KdmMailFetcher, j'utilise maintenant deux mécanismes : Spring, pour initier les tests comme des beans, ou bien initier l'assemblage des beans pour les tests d'intégration, et junitparams, pour faire des tests en série à partir d'un CSV.

JunitParams

J'utilise pour ma part, le fonctionnement qui consiste à alimenter les paramètres d'un test avec les lignes d'un fichier CSV. Permet de documenter les résultats de parcours de mails et des KDM que l'on est sensé y trouver.

La difficulté, pour le moment irrésolue, c'est que le csv qui paramètre ce test doit être défini de manière constante.

Piste : Utilisation des rules pour inclure Spring

Rappel fonctionnement des Rules
* Guide to JUnit 4 Rules

Je n'ai pas réussi à faire fonctionner le chargement par les rules.

  @ClassRule
  public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();

Ne produit rien, il doit falloir appeler quelque chose en plus.

Initialisation par Spring même lorsque le runner est différent.

Un exemle est donné, qui fonctionne avec le runner Parameterized.

 // [...]
 // Manually config for spring to use Parameterised
     private TestContextManager testContextManager;
	 
 // [...]
      @Before // Note 2
     public void setUp() throws Exception {
          this.testContextManager = new TestContextManager(getClass());
          this.testContextManager.prepareTestInstance(this);
     }

Cela fonctionne également avec mon exemple qui utilise le runner JunitParams.

Parameterized

Du coup, j'ai essayé de convertir le test en utilisant le runner Parameterized : fonctionne, mais ne résoud pas le problème de devoir charger l'emplacement dans une fonction statique, donc sans pouvoir utiliser le paramètre configuré par Spring.
Par ailleurs, on ne peut pas positionner une valeur statique en utilisant @Autowired.

Junit5

A noter que Parameterized a évolué dans JUnit5 et prévoit en particuleir une fonction pour les fichiers csv.

La mise en oeuvre demande une adaption des tests, et d'invalider le choix par défaut dans spring boot, qui utile Junit4.

Configuration de eclipse avec putty

24 March 2021

TODO compléter lorsque la solution aura été trouvée...

Suite à la mise à de eclipse en mars 2021, les clefs .ppk ne sont plus ouvertes correctement, la passphrase n'est pas demandée, et la connection echoue.

Pour pouvoir utiliser pageant, il faut configurer la variable d'environnement GIT_SSH
https://stackoverflow.com/questions/7762139/using-puttys-pagent-with-egit-in-eclipse

GIT_SSH="C:\Program Files\PuTTY\plink.exe"

L'erreur suivante est que eclipse n'identifie pas la clef du serveur distant comme étant enregistrée.

EBNF railroad representation

10 February 2021

En travaillant sur le workfow de production de code, je reprend la description de version sémantique, vers laquelle MAA m'avait orienté.

Cette description offre une jolie grammaire EBNF.

<valid semver> ::= <version core>
                 | <version core> "-" <pre-release>
                 | <version core> "+" <build>
                 | <version core> "-" <pre-release> "+" <build>

<version core> ::= <major> "." <minor> "." <patch>

<major> ::= <numeric identifier>

<minor> ::= <numeric identifier>

<patch> ::= <numeric identifier>

<pre-release> ::= <dot-separated pre-release identifiers>

<dot-separated pre-release identifiers> ::= <pre-release identifier>
                                          | <pre-release identifier> "." <dot-separated pre-release identifiers>

<build> ::= <dot-separated build identifiers>

<dot-separated build identifiers> ::= <build identifier>
                                    | <build identifier> "." <dot-separated build identifiers>

<pre-release identifier> ::= <alphanumeric identifier>
                           | <numeric identifier>

<build identifier> ::= <alphanumeric identifier>
                     | <digits>

<alphanumeric identifier> ::= <non-digit>
                            | <non-digit> <identifier characters>
                            | <identifier characters> <non-digit>
                            | <identifier characters> <non-digit> <identifier characters>

<numeric identifier> ::= "0"
                       | <positive digit>
                       | <positive digit> <digits>

<identifier characters> ::= <identifier character>
                          | <identifier character> <identifier characters>

<identifier character> ::= <digit>
                         | <non-digit>

<non-digit> ::= <letter>
              | "-"

<digits> ::= <digit>
           | <digit> <digits>

<digit> ::= "0"
          | <positive digit>

<positive digit> ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"

<letter> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J"
           | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T"
           | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d"
           | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n"
           | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x"
           | "y" | "z"
		   

Représentation railroad

J'aime beaucoup, par ailleurs, la représentation railroad des grammaires.

Le site bottlecap.de offre en particulier de jolies représentations.

Adaptation de la grammaire

Par contre, la syntaxe est légèrment différente sur le site.

Les terminaux sont indiqués sans les crochets <>, et ne peuvent pas comprendre d'espace.

En retirant les crochets, et en substituant les espaces par des '_', on obtient

validSemver ::= versionCore
                 | versionCore "-" preRelease
                 | versionCore "+" build
                 | versionCore "-" preRelease "+" build

versionCore ::= major "." minor "." patch

major ::= numeric_identifier

minor ::= numeric_identifier

patch ::= numeric_identifier

preRelease ::= dot-separated_pre-release_identifiers

dot-separated_pre-release_identifiers ::= pre-release_identifier
                                          | pre-release_identifier "." dot-separated_pre-release_identifiers

build ::= dot-separated_build_identifiers

dot-separated_build_identifiers ::= build_identifier
                                    | build_identifier "." dot-separated_build_identifiers

pre-release_identifier ::= alphanumeric_identifier
                           | numeric_identifier

build_identifier ::= alphanumeric_identifier
                     | digits

alphanumeric_identifier ::= non-digit
                            | non-digit identifier_characters
                            | identifier_characters non-digit
                            | identifier_characters non-digit identifier_characters

numeric_identifier ::= "0"
                       | positive_digit
                       | positive_digit digits

identifier_characters ::= identifier_character
                          | identifier_character identifier_characters

identifier_character ::= digit
                         | non-digit

non-digit ::= letter
              | "-"

digits ::= digit
           | digit digits

digit ::= "0"
          | positive_digit

positive_digit ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"

letter ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J"
           | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T"
           | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d"
           | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n"
           | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x"
           | "y" | "z"

Représentation graphique railroad


validSemver:




         ::= versionCore ( '-' preRelease )? ( '+' build )?


no references


versionCore:




         ::= major '.' minor '.' patch


referenced by:



major:




referenced by:



minor:




referenced by:



patch:




referenced by:



preRelease:




referenced by:



build:




referenced by:



pre-release_identifier:




         ::= alphanumeric_identifier

           | numeric_identifier


referenced by:



build_identifier:




         ::= alphanumeric_identifier

           | digits


referenced by:



alphanumeric_identifier:




referenced by:



numeric_identifier:




         ::= '0'

           | positive_digit digits?


referenced by:



identifier_characters:




referenced by:



identifier_character:




         ::= digit

           | non-digit


referenced by:



non-digit:




         ::= letter

           | '-'


referenced by:



digits:



digits   ::= digit+


referenced by:



digit:



digit    ::= '0'

           | positive_digit


referenced by:



positive_digit:




         ::= '1'

           | '2'

           | '3'

           | '4'

           | '5'

           | '6'

           | '7'

           | '8'

           | '9'


referenced by:



letter:



letter   ::= 'A'

           | 'B'

           | 'C'

           | 'D'

           | 'E'

           | 'F'

           | 'G'

           | 'H'

           | 'I'

           | 'J'

           | 'K'

           | 'L'

           | 'M'

           | 'N'

           | 'O'

           | 'P'

           | 'Q'

           | 'R'

           | 'S'

           | 'T'

           | 'U'

           | 'V'

           | 'W'

           | 'X'

           | 'Y'

           | 'Z'

           | 'a'

           | 'b'

           | 'c'

           | 'd'

           | 'e'

           | 'f'

           | 'g'

           | 'h'

           | 'i'

           | 'j'

           | 'k'

           | 'l'

           | 'm'

           | 'n'

           | 'o'

           | 'p'

           | 'q'

           | 'r'

           | 's'

           | 't'

           | 'u'

           | 'v'

           | 'w'

           | 'x'

           | 'y'

           | 'z'


referenced by:












 
... generated by RR - Railroad Diagram Generator


Using java artifacts with docker

08 February 2021

Article "Version Numbers for Continuous Delivery with Maven and Docker"

La réflexion se fait sur la base de l'article suivant :

L'utilisation de numéro de versions à la main est problématique dans un contexte d'automatisation, or “Every build is a potential release”.

L'article pointe également le fait que le maven-release-plugin pose des problème en générant trois cycles et autant de commits.

La préconisation est donc d'utiliser le hash du commit pour nommer chaque artefact. Pour ce faire, utilisation de git-commit-id-plugin. Ce plugin demande d'ajouter une configuration d'execution.

La suite de l'article montre comment générer l'artefact docker depuis maven ave fabric8, mais avec le commentaire postérieur qu'il serait plus malin de le faire avec jib en 2019.

Enfin, comme c'est l'artefact docker qui est généré, l'article propose d'inhiber le fait de pousser les .jar dans les repository locaux.

Pour le SI-DTP

Pour le développement du SI-DTP avec les outils de CI mis en place sur gitlab avec MMA, le problème est le fait qu'il n'y a pas de réutilisation d'artefacts java. Cela entraine plusieurs problèmes :

  • le build de certains projets est devenu complexe, obligeant à créer une superstructure avec des sous-modules pour référencer d'autres projets.
  • Pour les repository pures "bibliothèques", il n'y a pas de code de test, et la génération d'un conteneur docker est sans intéret
  • La non-réutilisation d'artefacts rends la compilation des projets sous-optimale.

Il faut distinguer deux cas, celui des produits "finis" qui font l'objet d'un service conteneurisé, et les libs, qui doivent être réutilisés pour la création des premiers.

Mise en oeuvre dans dicranum-api-client

Je rajoute la section suivante dans plugins :

            <plugin>
              <groupId>pl.project13.maven</groupId>
              <artifactId>git-commit-id-plugin</artifactId>
              <version>2.2.4</version>
              <executions>
                <execution>
                  <phase>validate</phase>
                  <goals>
                    <goal>revision</goal>
                  </goals>
                </execution>
              </executions>
              <configuration>
                <dateFormat>yyyyMMdd.HH'h'mm</dateFormat><!-- human-readable part of the version number -->
                <dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
                <generateGitPropertiesFile>false</generateGitPropertiesFile><!-- somehow necessary. otherwise the variables are not available in the pom -->
              </configuration>
            </plugin>

Et en tête de pom cela devient :

  <version>0.0.3+${version.number}</version>
  <properties>
    <version.number>${git.commit.time}.${git.commit.id.abbrev}</version.number>
  </properties>

Lorsque l'on fait un mvn package

[...]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ dicranum-api-client ---
[INFO] Building jar: C:\Users\hnl-bis\workspace\dicranum-api-client\target\dicranum-api-client-0.0.3+20210210.19h07.c272652.jar
[...]

OK

Par contre, lorsque je fais mvn install, les variables ne sont pas substituées.

[...]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ dicranum-api-client ---
[INFO] Installing C:\Users\hnl-bis\workspace\dicranum-api-client\target\dicranum-api-client-0.0.3+20210210.19h07.c272652.jar to C:\Users\hnl-bis\.m2\repository\fr\cst\rd\cannes2020\dicranum-api-client\0.0.3+${git.commit.time}.${git.commit.id.abbrev}\dicranum-api-client-0.0.3+${git.commit.time}.${git.commit.id.abbrev}.jar
[INFO] Installing C:\Users\hnl-bis\workspace\dicranum-api-client\pom.xml to C:\Users\hnl-bis\.m2\repository\fr\cst\rd\cannes2020\dicranum-api-client\0.0.3+${git.commit.time}.${git.commit.id.abbrev}\dicranum-api-client-0.0.3+${git.commit.time}.${git.commit.id.abbrev}.pom
[...]

En parcourant plusieurs articles de blog, ou sur stackoverflow le problème semble connu.

Comme dirait le poète, it's a feature, not a bug. Un des points importants est que le pom est déployé sur le repository et comprends le numéro de version. S'il n'est pas codé en dur, cela pose des problèmes pour les autres développements maven qui vont dépendre de ce pom.

La piste proposée dans l'article suivant, est de filtrer le pom au déploiement...

JBake broken with java > 9

08 February 2021

Considerer le fait de passer au plugin officiel (même s'il est mal documenté), il est inspiré du plugin angénieux :

Autre piste :

Prospection techno detection de présence

30 January 2021

Je cherche une solution de détection de présence pour allumer / éteindre le chauffage de mon fils (plus exactement augmenter ou baisser la temperature demandée dans la pièce). La présence de son téléphone n'est pas utilisable, car il n'apporte pas son téléphone au collège.

La première idée serait de mettre un tag RFID à son porte clef, qui ne traine jamais du boitier domoticz à plus de cinq mètres, entre la porte et le tiroir.

Il y a plusieurs discussions :
- la technologie à utiliser
- le modèle de tag / lecteur
- l'intégration

Technos

Le volume de données est faible (identification), la portée doit être de l'ordre de 5m, la lecture n'a pas besoin d'être rapide.

RFID

Il y a plusieurs techno, selon la fréquence, avec une porté variable. Dans l'absolu, le NFC est une sous-partie de RFID (Radio-Fréquence IDentification)

D'après wikipedia :

| catégorie | fréquence | vitesse | portée |
|-----------|---------------|--------------|-----------|
| LF | 120-150 kHz | lent | 10 à 50cm |
| HF | 13,56 mHz | lent | 10cm - 1m |
| UHF | 433 mHz | moyen | 1 - 100m |
| UHF | 865-868 mHz | rapide | 1 - 12m |
| SHF | 2450-5800 mHz | très rapide | 1 - 2m |
| ULB | 3,1 - 10 gHz | très rapide | >200m |

Les tags peuvent être 100% passifs, ou semi-actif ou actifs.

Les tags que l'on trouve dans le commerce sont soit à 125khz, soit à 13,56mHz

Beacon (Bluetooth)

https://learn.adafruit.com/pibeacon-ibeacon-with-a-raspberry-pi

Hyper simple à mettre en oeuvre, surtour que sur le RPi3, il y a une carte bluetooth configurée par défaut.

Web assembly pour Java

17 January 2021

http://blog.dmitryalexandrov.net/webassembly-for-java-developers/

Lancement de portainer dans docker tools

11 January 2021

docker run -d -p 9000:9000 -v //var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer

Le point important est le mappage de la socket. Noter la syntaxe utilisant //.

Modules java et outils

10 January 2021

https://koor.fr/Java/Tutorial/java_module.wp

Compilation de MQTT-Spy avec JavaFX

09 January 2021

Circonstances

Le Paho-MQTT est un projet d'Eclipse pour fournir une base dans pluisieurs langages pour utiliser MQTT. Parmi ces projets, mqtt-spy, un gui qui à l'air intéressant pour naviguer sur un serveur MQTT.

Difficulté, les projets ne compilent pas correctement sur ma station Ubuntu 20.04.

Compilation à partir des sources

https://github.com/eclipse/paho.mqtt-spy.git

$ mvn clean
$ mvn package 
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.inject.internal.cglib.core.$ReflectUtils$1 (file:/usr/share/maven/lib/guice.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of com.google.inject.internal.cglib.core.$ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
[INFO] Scanning for projects...
[WARNING] 
[WARNING] Some problems were encountered while building the effective model for org.eclipse.paho.mqttspy:mqtt-spy-daemon:jar:1.0.0
[WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-jar-plugin is missing. @ line 113, column 15
[WARNING] 
[WARNING] Some problems were encountered while building the effective model for org.eclipse.paho.mqttspy:mqtt-spy:jar:1.0.1-beta
[WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-jar-plugin is missing. @ line 134, column 15
[WARNING] 
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING] 
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING] 
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] spy-common                                                         [jar]
[INFO] spy-common-ui                                                      [jar]
[INFO] mqtt-spy-common                                                    [jar]
[INFO] mqtt-spy-daemon                                                    [jar]
[INFO] mqtt-spy                                                           [jar]
[INFO] mqtt-spy project                                                   [pom]
[INFO] 
[INFO] ----------------< org.eclipse.paho.mqttspy:spy-common >-----------------
[INFO] Building spy-common 1.0.1                                          [1/6]
[INFO] --------------------------------[ jar ]---------------------------------
[WARNING] The POM for com.sun.xml.bind:jaxb-impl:jar:2.2.11 is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details
[WARNING] The POM for com.sun.xml.bind:jaxb-xjc:jar:2.2.11 is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details
[WARNING] The POM for com.sun.xml.bind:jaxb-core:jar:2.2.11 is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details
[INFO] 
[INFO] --- maven-jaxb2-plugin:0.12.1:generate (spy-common) @ spy-common ---
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] spy-common 1.0.1 ................................... FAILURE [  5.913 s]
[INFO] spy-common-ui 1.0.1 ................................ SKIPPED
[INFO] mqtt-spy-common 1.0.1 .............................. SKIPPED
[INFO] mqtt-spy-daemon 1.0.0 .............................. SKIPPED
[INFO] mqtt-spy 1.0.1-beta ................................ SKIPPED
[INFO] mqtt-spy project 0.0.1-SNAPSHOT .................... SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  6.165 s
[INFO] Finished at: 2021-01-09T19:07:04+01:00
[INFO] ------------------------------------------------------------------------
---------------------------------------------------
constituent[0]: file:/usr/share/maven/conf/logging/
constituent[1]: file:/usr/share/maven/lib/maven-settings-3.x.jar
constituent[2]: file:/usr/share/maven/lib/maven-artifact-3.x.jar
constituent[3]: file:/usr/share/maven/lib/jsr250-api.jar
constituent[4]: file:/usr/share/maven/lib/maven-settings-builder-3.x.jar
constituent[5]: file:/usr/share/maven/lib/slf4j-api.jar
constituent[6]: file:/usr/share/maven/lib/wagon-http-shaded.jar
constituent[7]: file:/usr/share/maven/lib/maven-resolver-util.jar
constituent[8]: file:/usr/share/maven/lib/maven-repository-metadata-3.x.jar
constituent[9]: file:/usr/share/maven/lib/javax.inject.jar
constituent[10]: file:/usr/share/maven/lib/plexus-interpolation.jar
constituent[11]: file:/usr/share/maven/lib/commons-cli.jar
constituent[12]: file:/usr/share/maven/lib/maven-builder-support-3.x.jar
constituent[13]: file:/usr/share/maven/lib/sisu-inject.jar
constituent[14]: file:/usr/share/maven/lib/maven-plugin-api-3.x.jar
constituent[15]: file:/usr/share/maven/lib/maven-resolver-connector-basic.jar
constituent[16]: file:/usr/share/maven/lib/maven-model-builder-3.x.jar
constituent[17]: file:/usr/share/maven/lib/maven-shared-utils.jar
constituent[18]: file:/usr/share/maven/lib/maven-model-3.x.jar
constituent[19]: file:/usr/share/maven/lib/plexus-sec-dispatcher.jar
constituent[20]: file:/usr/share/maven/lib/wagon-provider-api.jar
constituent[21]: file:/usr/share/maven/lib/guice.jar
constituent[22]: file:/usr/share/maven/lib/maven-slf4j-provider-3.x.jar
constituent[23]: file:/usr/share/maven/lib/maven-embedder-3.x.jar
constituent[24]: file:/usr/share/maven/lib/plexus-utils.jar
constituent[25]: file:/usr/share/maven/lib/commons-lang3.jar
constituent[26]: file:/usr/share/maven/lib/jansi.jar
constituent[27]: file:/usr/share/maven/lib/maven-resolver-spi.jar
constituent[28]: file:/usr/share/maven/lib/commons-io.jar
constituent[29]: file:/usr/share/maven/lib/cdi-api.jar
constituent[30]: file:/usr/share/maven/lib/sisu-plexus.jar
constituent[31]: file:/usr/share/maven/lib/maven-resolver-provider-3.x.jar
constituent[32]: file:/usr/share/maven/lib/maven-compat-3.x.jar
constituent[33]: file:/usr/share/maven/lib/maven-core-3.x.jar
constituent[34]: file:/usr/share/maven/lib/aopalliance.jar
constituent[35]: file:/usr/share/maven/lib/plexus-component-annotations.jar
constituent[36]: file:/usr/share/maven/lib/maven-resolver-transport-wagon.jar
constituent[37]: file:/usr/share/maven/lib/maven-resolver-impl.jar
constituent[38]: file:/usr/share/maven/lib/guava.jar
constituent[39]: file:/usr/share/maven/lib/wagon-file.jar
constituent[40]: file:/usr/share/maven/lib/maven-resolver-api.jar
constituent[41]: file:/usr/share/maven/lib/plexus-cipher.jar
constituent[42]: file:/usr/share/maven/lib/jcl-over-slf4j.jar
---------------------------------------------------
Exception in thread "main" java.util.ServiceConfigurationError: com.sun.tools.xjc.Plugin: com.sun.tools.xjc.addon.accessors.PluginImpl Unable to get public no-arg constructor
	at java.base/java.util.ServiceLoader.fail(ServiceLoader.java:584)
	at java.base/java.util.ServiceLoader.getConstructor(ServiceLoader.java:675)
	at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1234)
	at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1266)
	at java.base/java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1301)
	at java.base/java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1386)
	at com.sun.tools.xjc.Options.findServices(Options.java:960)
	at com.sun.tools.xjc.Options.getAllPlugins(Options.java:374)
	at com.sun.tools.xjc.Options.parseArgument(Options.java:688)
	at com.sun.tools.xjc.Options.parseArguments(Options.java:812)
	at org.jvnet.mjiip.v_2.OptionsFactory.createOptions(OptionsFactory.java:87)
	at org.jvnet.mjiip.v_2.OptionsFactory.createOptions(OptionsFactory.java:18)
	at org.jvnet.jaxb2.maven2.RawXJC2Mojo.doExecute(RawXJC2Mojo.java:462)
	at org.jvnet.jaxb2.maven2.RawXJC2Mojo.execute(RawXJC2Mojo.java:311)
	at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:210)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:156)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:148)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
	at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56)
	at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:305)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192)
	at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105)
	at org.apache.maven.cli.MavenCli.execute(MavenCli.java:957)
	at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:289)
	at org.apache.maven.cli.MavenCli.main(MavenCli.java:193)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:282)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:225)
	at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:406)
	at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:347)
Caused by: java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlAccessType
	at java.base/java.lang.Class.getDeclaredConstructors0(Native Method)
	at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3215)
	at java.base/java.lang.Class.getConstructor0(Class.java:3420)
	at java.base/java.lang.Class.getConstructor(Class.java:2165)
	at java.base/java.util.ServiceLoader$1.run(ServiceLoader.java:662)
	at java.base/java.util.ServiceLoader$1.run(ServiceLoader.java:659)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:554)
	at java.base/java.util.ServiceLoader.getConstructor(ServiceLoader.java:670)
	... 34 more
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlAccessType
	at org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy.loadClass(SelfFirstStrategy.java:50)
	at org.codehaus.plexus.classworlds.realm.ClassRealm.unsynchronizedLoadClass(ClassRealm.java:271)
	at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:247)
	at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:239)
	... 42 more

J'ai exploré plusieurs points :

Installation du package javafx :

Le package openjfx est installé.

$ java -version 
openjdk version "14.0.2" 2020-07-14
OpenJDK Runtime Environment (build 14.0.2+12-Ubuntu-120.04)
OpenJDK 64-Bit Server VM (build 14.0.2+12-Ubuntu-120.04, mixed mode, sharing)
$ javac -version
javac 14.0.2

Le "niveau de version" de java est lui contraint à java 8 dans les fichiers pom :

Essai de compilation d'un autre projet

https://github.com/eclipse/paho.mqtt.java.git

Ne fonctionne pas non plus, mais avec une erreur très différente :

Exception in thread "main" java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:513)
	at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:525)
Caused by: java.lang.RuntimeException: Class java/lang/UnknownError could not be instrumented.
	at org.jacoco.agent.rt.internal_28bab1d.core.runtime.ModifiedSystemClassRuntime.createFor(ModifiedSystemClassRuntime.java:140)
	at org.jacoco.agent.rt.internal_28bab1d.core.runtime.ModifiedSystemClassRuntime.createFor(ModifiedSystemClassRuntime.java:101)
	at org.jacoco.agent.rt.internal_28bab1d.PreMain.createRuntime(PreMain.java:55)
	at org.jacoco.agent.rt.internal_28bab1d.PreMain.premain(PreMain.java:47)
	... 6 more
Caused by: java.lang.NoSuchFieldException: $jacocoAccess
	at java.base/java.lang.Class.getField(Class.java:2013)
	at org.jacoco.agent.rt.internal_28bab1d.core.runtime.ModifiedSystemClassRuntime.createFor(ModifiedSystemClassRuntime.java:138)
	... 9 more
*** java.lang.instrument ASSERTION FAILED ***: "result" with message agent load/premain call failed at src/java.instrument/share/native/libinstrument/JPLISAgent.c line: 422
FATAL ERROR in native method: processing of -javaagent failed, processJavaStart failed
Aborted (core dumped)

D'autres erreurs similaires suivent.

Essai d'execution du package précompilé

Récupération des jar précompilés.

$ java -jar mqtt-spy-1.0.0.jar 
Erreur : impossible de trouver ou de charger la classe principale pl.baczkowicz.mqttspy.Main
Causé par : java.lang.NoClassDefFoundError: javafx/application/Application

Bon, ici, on peut supposer que la dépendance à javafx n'est pas trouvée.

$ java --module-path=/usr/share/openjfx/lib -jar mqtt-spy-1.0.0.jar
Erreur : impossible de trouver ou de charger la classe principale pl.baczkowicz.mqttspy.Main
Causé par : java.lang.NoClassDefFoundError: javafx/application/Application

Ici, j'ajoute les dépendances sous forme de module pour pouvoir indiquer seulement le répertoire racine des .jar.

Apparement, il manque encore qq chose :

$ java --module-path=/usr/share/openjfx/lib --add-modules javafx.controls -jar mqtt-spy-1.0.0.jar
Exception in Application start method
java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Exception in Application start method
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
	at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
	at pl.baczkowicz.mqttspy.configuration.MqttConfigurationManager.<init>(MqttConfigurationManager.java:69)
	at pl.baczkowicz.mqttspy.Main.start(Main.java:63)
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
	at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
	at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
	at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
	... 1 more
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBException
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
	... 11 more
Exception running application pl.baczkowicz.mqttspy.Main

Ici, le module est satisfait, on a ajouté un module path, mais aussi le module à prendre en compte. MAIS, apparement il manque les bibliotèques natives, essayons avec celles du jdk...

$ java -Djava.library.path=/usr/lib/jvm/java-14-openjdk-amd64/lib/ --module-path=/usr/share/openjfx/lib --add-modules javafx.controls -jar mqtt-spy-1.0.0.jar
Graphics Device initialization failed for :  es2, sw
Error initializing QuantumRenderer: no suitable pipeline found
java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
	at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer.getInstance(QuantumRenderer.java:280)
	at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.init(QuantumToolkit.java:222)
	at javafx.graphics/com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:260)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:409)
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
	at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:94)
	at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:124)
	at java.base/java.lang.Thread.run(Thread.java:832)
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: No toolkit found
	at javafx.graphics/com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:272)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:409)
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
	... 5 more

Indique -Djavafx.verbose=true -Dprism.verbose=true pour en savoir plus.

D'après un post stackoverflow, cela peut-être lié au manque d'un paquet gtk :

https://stackoverflow.com/questions/21185156/javafx-on-linux-is-showing-a-graphics-device-initialization-failed-for-es2-s
En particulier, un des commentaires conseille d'installer ceci :

sudo apt-get update; sudo apt-get install libswt-gtk-3-java

Problème, ce package n'existe par sur Ubuntu 20.04.

Par contre :

sudo apt-get install libswt-gtk-4-java

$ java -Dprism.verbose=true -Djava.library.path=/usr/lib/jvm/java-14-openjdk-amd64/lib/ --module-path=/usr/share/openjfx/lib --add-modules javafx.controls -jar mqtt-spy-1.0.0.jar
[...]
*** Fallback to Prism SW pipeline
Prism pipeline name = com.sun.prism.sw.SWPipeline
GraphicsPipeline.createPipeline failed for com.sun.prism.sw.SWPipeline
java.lang.UnsatisfiedLinkError: no prism_sw in java.library.path: [/usr/lib/jvm/java-14-openjdk-amd64/lib/]
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2670)
[...]
$ find /usr/ -iname "prism_es2"
/usr/lib/x86_64-linux-gnu/jni/libprism_es2.so
$ java -Dprism.verbose=true -Djava.library.path=/usr/lib/jvm/java-14-openjdk-amd64/lib/:/usr/lib/x86_64-linux-gnu/jni/ --module-path=/usr/share/openjfx/lib --add-modules javafx.controls -jar mqtt-spy-1.0.0.jar

[...]
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBException
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
[...]

Problème, la classe java.xml.bind ne fait plus partie du jdk depuis java 9.

*Journal du net : Comment corriger l'erreur java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

... j'abandonne..

Le problème est référencé dans une issue

CSVKit en ligne de commande

05 January 2021

Utilisation d'un outil en ligne de commande.

Cas exemple

Pour montrer l'utilisation de l'outil, je note l'utilisation que j'ai pu en faire hier pour récupérer des liens qui étaient dans un tableau.

J'ai tout d'abord transformé le tableau en explosant chacune des lignes qui contenait plusieurs documents vers plusieurs lignes, et en ajoutant une colonne pour désigner le nom de la partie.

Voici un extrait du fichier exemple :

Nom;Prénom;Mail;Partie;Contribution;NomFichier
Ballu;Claire;balluclaire17@hotmail.fr;all;https://www.cst.fr/wp-content/uploads/elementor/forms/5fdbcf28c1c85.pdf;Ballu-Claire-all
NDZANA NDZANA;Lambert Marie;lndzana@gmail.com;numéro 1;https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb1ee7b321a.pdf;NDZANA_NDZANA-Lambert_Marie-numéro_1
NDZANA NDZANA;Lambert Marie;lndzana@gmail.com;numéro 2;https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb1f7b69ce3.pdf;NDZANA_NDZANA-Lambert_Marie-numéro_2
NDZANA NDZANA;Lambert Marie;lndzana@gmail.com;numéro 3;https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb1fe20236c.pdf;NDZANA_NDZANA-Lambert_Marie-numéro_3
NDZANA NDZANA;Lambert Marie;lndzana@gmail.com;numéro 4;https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb202f41da5.pdf;NDZANA_NDZANA-Lambert_Marie-numéro_4
NDZANA NDZANA;Lambert Marie;lndzana@gmail.com;numéro 5;https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb206abf487.pdf;NDZANA_NDZANA-Lambert_Marie-numéro_5
NDZANA NDZANA;Lambert Marie;lndzana@gmail.com;numéro 6;https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb20a8daff3.pdf;NDZANA_NDZANA-Lambert_Marie-numéro_6
NDZANA NDZANA;Lambert Marie;lndzana@gmail.com;numéro 7;https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb21e2b6d92.pdf;NDZANA_NDZANA-Lambert_Marie-numéro_7
NDZANA NDZANA;Lambert Marie;lndzana@gmail.com;numéro 8;https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb67eab4ffa.pdf;NDZANA_NDZANA-Lambert_Marie-numéro_8
KABORE;Bangbi Francis Frédéric;kaborefr@gmail.com;Attestation de soutenance;https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb2fe8c1ecd.pdf;KABORE-Bangbi_Francis_Frédéric-Attestation_de_soutenance
KABORE;Bangbi Francis Frédéric;kaborefr@gmail.com;Mémoire+CV;https://we.tl/t-vyItbbciLB;KABORE-Bangbi_Francis_Frédéric-Mémoire_CV
CAVRET;Margot;margot.cavret@laposte.net;all;https://www.cst.fr/wp-content/uploads/elementor/forms/5fdaa0733dd8b.pdf;CAVRET-Margot-all
[...]
Marion;Phillipe;;all;en attente;Marion-Phillipe-all

Pour avoir un joli affichage des prenom, nom, partie, lien :

> csvcut -d ";" -c 2,1,4,5 Récap_contributions_rencontres_chercher_créer-2021-01-04-17h34-HNL-UTF-8.csv |csvlook
| Prénom                  | Nom                 | Partie                               | Contribution                                                             |
| ----------------------- | ------------------- | ------------------------------------ | ------------------------------------------------------------------------ |
| Claire                  | Ballu               | all                                  | https://www.cst.fr/wp-content/uploads/elementor/forms/5fdbcf28c1c85.pdf  |
| Lambert Marie           | NDZANA NDZANA       | numéro 1                             | https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb1ee7b321a.pdf  |
| Lambert Marie           | NDZANA NDZANA       | numéro 2                             | https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb1f7b69ce3.pdf  |
| Lambert Marie           | NDZANA NDZANA       | numéro 3                             | https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb1fe20236c.pdf  |
| Lambert Marie           | NDZANA NDZANA       | numéro 4                             | https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb202f41da5.pdf  |
| Lambert Marie           | NDZANA NDZANA       | numéro 5                             | https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb206abf487.pdf  |
| Lambert Marie           | NDZANA NDZANA       | numéro 6                             | https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb20a8daff3.pdf  |
| Lambert Marie           | NDZANA NDZANA       | numéro 7                             | https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb21e2b6d92.pdf  |
| Lambert Marie           | NDZANA NDZANA       | numéro 8                             | https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb67eab4ffa.pdf  |
| Bangbi Francis Frédéric | KABORE              | Attestation de soutenance            | https://www.cst.fr/wp-content/uploads/elementor/forms/5fdb2fe8c1ecd.pdf  |
| Bangbi Francis Frédéric | KABORE              | Mémoire+CV                           | https://we.tl/t-vyItbbciLB                                               |
| Margot                  | CAVRET              | all                                  | https://www.cst.fr/wp-content/uploads/elementor/forms/5fdaa0733dd8b.pdf  |
[...]
| Phillipe                | Marion              | all                                  | en attente                                                               |

L'option -d permet de changer le séparateur par défaut, -c de préciser les colonnes que l'on veut afficher.

On peut faire plus propre, en conformant le fichier en entrée du pipe

>csvclean Récap_contributions_rencontres_chercher_créer-2021-01-04-17h34-HNL.csv
Your file is not "utf-8-sig" encoded. Please specify the correct encoding with the -e flag or with the PYTHONIOENCODING environment variable. Use the -v flag to see the complete error.

>csvclean -e ansi Récap_contributions_rencontres_chercher_créer-2021-01-04-17h34-HNL.csv
1 error logged to Récap_contributions_rencontres_chercher_créer-2021-01-04-17h34-HNL_err.csv

>csvclean -e ansi -d ";" Récap_contributions_rencontres_chercher_créer-2021-01-04-17h34-HNL.csv
No errors.

Créé un fichier Récap_contributions_rencontres_chercher_créer-2021-01-04-17h34-HNL_out.csv, ou le séparateur est remplaçé par une virgule. Les fins de lignes et l'encodage des caractères n'est pas changé, bug ?

Référence

Musique sur linux divers liens sur Jack et Swami

18 October 2020

Liste de logiciels utilisant Jack

Projet à regarder de plus près

Pour l'édition de fontes

Liens sur la construction d'orgue

18 October 2020

Mesure de performances sous linux

18 October 2020

Je cherche des pistes pour comprendre pourquoi l'ordinateur "trasche" parfois.

Mesure de performances

https://linuxconfig.org/how-to-benchmark-your-linux-system

sysbench semble être une piste pour mesurer les problèmes.

Lorsque je passe les tests de perfs, les résultats ne sont pas mauvais...

A noter hardinfo pour une synthèse graphique du matériel installé.

https://askubuntu.com/questions/988629/problem-with-swapper-process-cpu-utilization

Mentionne l'outil perf interessant :

sudo perf record -g -a sleep 10

On peut ensuite generer un rapport

Changement du swapiness

cat /proc/sys/vm/swappiness

Par défaut à 60

Je teste

sudo sysctl vm.swappiness=10

Pour limiter le swap...

Dissipation Raspberry-Pi 4

18 October 2020

https://www.minimachines.net/actu/dissipation-raspberry-pi-4-83641

Portable XML validation

18 September 2020

L'objet est d'avoir une chaine de validation pour les documents XML faisant appel a des schémas XML complexes, eux-même dépendant d'autres schémas.

Mon besoin de base : un outil permettant d'afficher des erreurs dans un document à partir d'un fichier XML, d'un schéma, et d'un catalog poitant sur d'autres schémas dasn des sous-répertoires.

Outils

Les outils de validation XML identifiés sont les suivants :

  • XMLLint (basé sur libxml)
  • Xerces qui se décline
  • xerces (java)
  • xerces-c (C++)
  • xerces-p
  • xerces2-j (refonte en java)
  • xml-validator ???

J'ai utilisé ses outils dans le passé, mais entre les mises à jour d'os et les changments de machine, difficile d'avoir une chaine stable.
L'idée est donc d'avoir un conteneur et de scripter la validation.

Une autre approche langage est possible avec quelques lignes de java.

Essais

Utilisation de xmllint (via docker)

$ docker run -i --rm -v $PWD:/data --env XML_CATALOG_FILES=/data/catalog.xml mribeiro/xmllint --debugent --noout --schema reference/smpte/st2067-3-2016/st2067-3a-2016.xsd reference/smpte/st2067-3-2016/st2067-3b-2016.xml

On obtient les logs suivants :

new input from file: reference/smpte/st2067-3-2016/st2067-3a-2016.xsd
reference/smpte/st2067-3-2016/st2067-3a-2016.xsd:120: element element: Schemas parser error : Element '{http://www.w3.org/2001/XMLSchema}element', attribute 'ref': The QName value '{http://www.w3.org/2000/09/xmldsig#}Signature' does not resolve to a(n) element declaration.
reference/smpte/st2067-3-2016/st2067-3a-2016.xsd:66: element element: Schemas parser error : element decl. '{http://www.smpte-ra.org/schemas/2067-3/2016}Id', attribute 'type': The QName value '{http://www.smpte-ra.org/schemas/433/2008/dcmlTypes/}UUIDType' does not resolve to a(n) type definition.
...

La validation du schéma fonctionne, mais, le problème semble être que les éléments du catalog ne sont pas pris en compte, les entités ne sont pas résolues.

Quelques changements au catalog n'y font rien :
- ajout du prefixe file:// au fichiers
- ajout du path absolu (vu depuis docker)

Mise en place de maven sur gitlab

13 September 2020

gitlab propose une fonctionnalité de packages, permettant de stocker des artefacts java. J'essaye de voir si cette fonctionnalité permet de continuer a travailler en découpant les projets en lib et utilisation.

Les instructions principales :

  • GitLab Maven Repository
    Contenu équivalent à un autre endroit :
  • GitLab Maven Repository
    Creation d'un projet minimaliste avec une classe comportant une seule fonction, say, qui ajoute "Hello, " et "!" au nom passé en paramètre.

En local, la compilation maven fonctionne et génère un .jar contenant le code.

En suivant le instruction, on trouve un exemple de fichier pour la ci :

Il faut bien sur renommer le fichier .gitlab-ci.yml

Le premier upload avec le fichier provoque une erreur de la CI :

java.io.FileNotFoundException: The specified user settings file does not exist: /builds/cstis-experimental/maven-artifact/test-package-dependency/ci_settings.xml

Problème avec le workspace Eclipse

12 September 2020

Mon installation Eclipse n'a plus voulu démarrer un jour...

Quelques notes sur ce que j'ai pu faire.

Tout d'abord, le premier reflexe, c'est de démarrer avec l'option -clean.

Sous windows, on peut se rendre dans le répertoire d'installation et taper

> eclipsec.exe -clean

Pour en savoir plus, on peut ajouter :

> eclipsec.exe -clean -debug -consoleLog

Mais rien n'y a fait.

Les options :
https://eclipsesource.com/blogs/2013/08/22/eclipse-command-line-options/

J'ai refait une installation de Eclipse avec une version plus récente (juin 2020).
J'ai le même diagnostic au démarrage.

Par contre, en creant un nouveau workspace, la nouvelle installation fonctionne parfaitement.

Sauvegarde

Avant d'aller plus loin, j'ai fait une sauvegarde de mon workspace. Il y a des fichiers en erreurs...

Tripatouillage du workspace

J'ai copié .medata vers POINTmetadata, et redémarré.

Eclipse démarre, mais je me retrouve, sans mes préférences, sans mes projets, sans mes working sets, sans mes lancements prédéfinis.

Projets

Je réimporte tous les projets, ils apparraisent (tous ouverts...).

Préférences

Je n'ai pas pu faire les différentes étapes mentionnées, mais la restauration des préfrences à (partiellement fonctionnée).

https://blog.pdark.de/2011/09/02/restoring-a-corrupted-workspace-in-eclipse/

Copie de

POINTmetadata/.plugins/org.eclipse.core.runtime/.settings

dans

.metadata/.plugins/org.eclipse.core.runtime/

Working sets

Je copie les fichiers de

POINTmetadata/.plugins/org.eclipse.ui.workbench

dans

.metadata/.plugins/org.eclipse.ui.workbench

https://stackoverflow.com/questions/41781157/where-is-the-workingset-info-of-an-eclipse-workspace-stored

lancements

Je copie le répertoire et tous les fichiers .launches qu'il contient :

POINTmetadata/.plugins/org.eclipse.debug.core/.launches

dans

.metadata/.plugins/org.eclipse.debug.core/

https://stackoverflow.com/questions/36652746/where-are-run-configurations-stored

Autres références

https://web.archive.org/web/20180612185409/http://blog.chris-alex-thomas.com:80/2009/08/26/eclipse-refuses-to-start-again/
http://underlap.blogspot.com/2011/10/eclipse-dance-steps.html

Home Assitant with a Tasmota sensor

13 July 2020

Documentation

Exemple de configuration donnée :

# Example configuration.yaml entry
sensor:
  - platform: mqtt
    name: "Timer 1"
    state_topic: "tele/sonoff/sensor"
    value_template: "{{ value_json.Timer1.Arm }}"
    json_attributes_topic: "tele/sonoff/sensor"
    json_attributes_template: "{{ value_json.Timer1 | tojson }}"
  - platform: mqtt
    name: "Timer 2"
    state_topic: "tele/sonoff/sensor"
    value_template: "{{ value_json.Timer2.Arm }}"
    json_attributes_topic: "tele/sonoff/sensor"
    json_attributes_template: "{{ value_json.Timer2 | tojson }}"

D'autres informations plus génériques :

sensor:
  - platform: mqtt
    name: "Tasmota Temperature"
    state_topic: "tele/tasmota/SENSOR"
    value_template: "{{ value_json['DHT22'].Temperature }}"
    unit_of_measurement: "°C"  # "F" if using Fahrenheit
    availability_topic: "tele/tasmota/LWT"
    payload_available: "Online"
    payload_not_available: "Offline"
    device_class: temperature
  - platform: mqtt
    name: "Tasmota Humidity"
    state_topic: "tele/tasmota/SENSOR"
    value_template: "{{ value_json['DHT22'].Humidity }}"
    unit_of_measurement: "%"
    availability_topic: "tele/tasmota/LWT"
    payload_available: "Online"
    payload_not_available: "Offline"
    device_class: humidity

Récupération des informations envoyées par Tasmota

$ mosquitto_sub -t "#" -v |grep tasmota
tele/tasmota_5FBCCA/LWT Connecté
tele/tasmota_B2431F/LWT Connecté
tele/tasmota_B2431F/STATE {"Time":"2020-07-13T16:38:49","Uptime":"1T01:50:11","UptimeSec":93011,"Heap":27,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":19,"MqttCount":1,"POWER":"OFF","Wifi":{"AP":1,"SSId":"Wifi_HNL_IV_2263","BSSId":"F4:CA:E5:E3:9B:48","Channel":10,"RSSI":100,"Signal":-33,"LinkCount":1,"Downtime":"0T00:00:05"}}
tele/tasmota_5FBCCA/STATE {"Time":"2020-07-13T16:39:17","Uptime":"0T00:05:13","UptimeSec":313,"Heap":28,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":19,"MqttCount":1,"Wifi":{"AP":1,"SSId":"Wifi_HNL_IV_2263","BSSId":"F4:CA:E5:E3:9B:48","Channel":10,"RSSI":100,"Signal":-24,"LinkCount":1,"Downtime":"0T00:00:07"}}
tele/tasmota_5FBCCA/SENSOR {"Time":"2020-07-13T16:39:17","SI7021":{"Temperature":26.7,"Humidity":39.9,"DewPoint":11.9},"TempUnit":"C"}

Le contenu du topic tele/tasmota_5FBCCA/SENSOR :

{
  "Time":"2020-07-13T16:39:17",
  "SI7021":
    {
      "Temperature":26.7,
      "Humidity":39.9,
      "DewPoint":11.9
    },
    "TempUnit":"C"
}
# HNL sensor configuration
sensor:
  - platform: mqtt
    name: "Tasmota Temperature"
    state_topic: "tele/tasmota_5FBCCA/SENSOR"
    value_template: "{{ value_json['SI7021'].Temperature }}"
    unit_of_measurement: "°C"  # "F" if using Fahrenheit
    availability_topic: "tele/tasmota_5FBCCA/SENSOR"
    payload_available: "Online"
    payload_not_available: "Offline"
    device_class: temperature
  - platform: mqtt
    name: "Tasmota Humidity"
    state_topic: "tele/tasmota_5FBCCA/SENSOR"
    value_template: "{{ value_json['SI7021'].Humidity }}"
    unit_of_measurement: "%"
    availability_topic: "tele/tasmota_5FBCCA/SENSOR"
    payload_available: "Online"
    payload_not_available: "Offline"
    device_class: humidity

Projet domotique - Capteur DHT 22 - Tasmota

11 July 2020

L'idée ici, est de créer une nouvelle sonde avec un Node MCU, mais d'utiliser le firmware Tasmota. L'idée est d'y trouver une plus grande souplesse de fonctionnement qu'en utilisant le programmme avec les variables et le comportement flashées en dur.

Flashage

Branchement du NodeMCU sur le port USB

Lancement de tasmotizer.py (version 1.0)

Flashage avec l'image de tasmota que j'ai : tasmota-FR.bin

esptool.py v2.8
Found 1 serial ports
Serial port /dev/ttyUSB0
Connecting....
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: ec:fa:bc:5f:bc:ca
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Erasing flash (this may take a while)...
Chip erase completed successfully in 8.7s
Flash params set to 0x0340
Compressed 581824 bytes to 400821...
Wrote 581824 bytes (400821 compressed) at 0x00000000 in 35.5 seconds (effective 131.1 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...

Le NodeMCU flashé ne pouvait pas se connecter.

Je recommence avec une autre version du firmware, qui est la version 8.3.1.

Ne peut toujours pas se connecter, mais c'est une erreur de ma part, je n'avais laissé qu'une position possible pour le bail du wifi...

Configuration

Dans un premier temps, on peut utiliser tasmotizer pour envoyer un fichier de configuration vers le Node MCU tasmotizé. Cela permet de configurer le wifi, et quelques basiques sur MQTT.

Il faut ensuite identifier son addresse.

Dans un second temps, on accède à une interface Web en appelant l'addresse du Node MCU tasmotizé lui même.

Configuration du DHT 22

Le DHT 22 est connu sous le nom de SI7021
* Issue github Tasmota : No DHT22 sensor selectable

On peut le paramétrer sur la pin D4.

J'ajoute aussi l'envoi d'informations pour domoticz.
Le tasmota envoie :

domoticz/in {"idx":12,"nvalue":0,"svalue":"24.80;34.30;2","Battery":100,"RSSI":10}

Ce qui est beaucoup plus complet que les informations que j'envoyais moi même. Il y a les infos de batterie et de force du signal.

Projet domotique - Capteur DHT 22

09 July 2020

Cadre du projet

Cette note s'inscrit dans le cadre d'un projet pour m'amuser. J'y vois déjà des tas de prolongements possibles.

J'ai essayé de lister les objectifs de ce projet, histoire de ne pas partir dans tous les sens.

  • M'amuser (ce qui excusera beaucoup de disgressions et de choix improductifs
  • Monitorer les température et humidité du garage et de la chambre de n°2 dans les combles.
  • Me maintenir au courant sur les pratiques de l'IOT

L'idée est de n'utiliser que des logiciels libres et de maintenir un minimum de sécurité au terme de l'installation. Je m'autorise à faire des choix plus trashs dans la partie exploratoire.

Ingrédients

L'idée est d'avoir des capteurs dans la maison, dans un premier temps, et de trouver un moyen efficace de les relier.

En vrac, voici les ingrédients :

  • ESP8266 pour les capteurs et DHT 22 pour la mesure température / humidité
  • Broker MQTT pour découpler les capteurs d'un éventuelle superviseur.
  • Un logiciel de centralisations des informations. Le premier choix s'est porté sur domoticz, mais je vais regarder d'autres logiciels.

Mise en oeuvre

Matériel utilisé

  • Raspberry 4 (a remplacer par un modèle antérieur, pour ne pas avoir de problème de ventilateur).
  • Node MCU
  • DHT 22

Pour le moment, j'utilise l'ordinateur principal de la maison pour le broker MQTT et le travail de prototypage de l'adaptation.

Logiciels utilisés

  • Sur le Rpi

    • OS Raspbian buster pour le RPi 4
    • Domoticz sur ce même Rpi
  • Sur l'ordinateur de développement

    • Ubuntu 18.04
    • IDE arduino (à mettre à jour manuellement pour avoir une version sufisament récente)
    • nodered pour prototyper l'interfaçage MQTT / Domoticz

Just go

Il s'agit de brancher correctement le DHT 22 sur le Node Mcu.

Je décide de suivre l'exemple suivant, qui parrait correctement détaillé et illustré :

Cablage

Premier eccueil, il ne s'agit pas du même modèlé de ESP82266, le montage proposé utilise un LoLin WeMos D1 Mini, mais bon en suivant le brochage du Node MCU, on s'en sort.

Il n'y a pas d'entrée 5V. La broche Vin correspond à autre chose. Je branche sur une broche 3,3V. A vérifier, s'il l'on a la même précision dans ces conditions.

Le Node MCU est pénible à brancher sur une plaque breadboard, puisqu'il prend toute la hauteur des lignes verticales, et qu'il ne reste pas pour brancher quoi que ce soit. Heureusement, on faisant attention au brochage, on voit que l'on peut ne brancher qu'un coté dans la breadboard.

Montage node MCU, DHT 22 et LED sur breadboard

Derniere aventure, n'ayant pas de résistance 220 ohm, je met en série deux résitances de 100 ohms.

Flashage

La version 1.0.5 de arduino installée sur mon ordinateur est trop ancienne. Contrairement à ce que l'on pourrait croire en regardant le numéro de version, il s'agit d'une version plus ancienne.

Arduino version 2:1.0.5

Les préférences ne permettent pas d'afficher un champ de saisi d'URL :

Préférences Arduino 2:1.0.5

J'installe en local dans un répertoire temporaire la version XXXX.

Arduino version 1.8.13
Préférénces Arduino 1.8.13

Ensuite, il faut installer un nouveau fournisseur de board dans les préférences et redémarrer l'IDE. On peut ensuite accéder au boards ESP82266 comme cible.

Il y a plus de détails sur cette opération ici :

Il faut également installer les bibliothèques. La bibliothque DHT est celle d'Adafruit, qu'il faut installer avec les dépendances.

Je créé un Wifi invité et le place le SSID et mdp dans le sketch. Je laisse les user / mdp vides pour la connexion à MQTT.

Je vois un premier problème ici : il faut configurer le sketch à chaque fois que l'on change la configuration.

Ls messages de debug sur la sortie série sont utilies pendant la mise au point. Ne bas oublier de régler le bon débit pour avoir un affichage correct.

Fonctionne casiment du premier coup.

Pour monitorer les envois sur MQTT :

> mosquitto_sub -t "#" -v
sensor/temperature 21.90
sensor/humidity 71.90

Configuration de domoticz pour recevoir les données du capteurs

Ajout d'une passerelle MQTT avec le serveur de la machine de développement.

Domoticz - Ajout d'un hardware MQTT

Ajout d'un hardware "Virtual" pour pouvoir ajouter des périphériques virtuels.

Domoticz - Ajout d'un hardware Virtual

On obtient la liste de hardware suivants :

Domoticz - Liste des hardwares configurés

On peut ajouter des périphériques virtuels, en appuyant sur le bouton, et en choisissant un type de device.

Domoticz - Ajout d'un device Virtual

Après l'ajout d'un bouton, d'une sonde de temperature, une sonde d'humidité et une sonde combinée temperature/humidité.

Domoticz - Liste de devices

Les devices sur la page d'acceuil.

Domoticz - Tableau de bord

La courbe de suivi de l'humidité.

Domoticz - Courbe de suivi humidité

MQTT et NodeRed pour mapper les informations pour domoticz

La programmation du NodeMCU est faite de la manière suivante :

Les topics publiés sont :

sensor/temperature 22.40
sensor/humidity 51.60

Et le topic consommé suivant :

homeassistant/switch1

La valeur pouvant être ON ou OFF.

Et domoticz veut souscrit au topic suivant :

domoticz/in

Et publie sur

domoticz/out

Je bidouille dans NodeRed pour publier à partir des messages du capteur les entrées suivantes :

domoticz/in {"idx":10,"svalue":"22.50 C"}
domoticz/in {"idx":9,"nvalue":52,"svalue":"0"}

NodeRed Capteurs séparés

Comme Domoticz peut également entrer les deux valeurs sur un même capteur, je bricole aussi une intégration des deux signaux entrants, ce qui donne :

sensor/dht22 {"sensor/humidity":"52.30","sensor/temperature":"21.10"}

Enfin, j'utilise l'API du genre restful de Domoticz pour écrire directement les valeurs intégrées du capteur.

J'ajoute une commande du switch pour faire l'accusé de reception des données.

JPA, mysql and identification stratégies

08 May 2020

Identification des différents niveaux d'abstraction

Une part de la complexité vient du fait que l'on met ensemble plusieurs couches logicielles.

  • JPA est le framework standardisé de gestion de la persistance, correspondant à une JSR java.
  • Hibernate est le framework historique, qui est une implémentation de JPA, mais qui sur certains aspects proposent des fonctions supplémentaires.
  • La base de donnée
    • MySql
    • MariaDb

Les stratégies d'identité JPA

On peut distinguer les stratégies suivantes : AUTO, IDENTITY, SEQUENCE, ou TABLE

Avec la stratégie AUTO, hibernate ajoute une table hibernate_sequence pour avoir une séquence absolue, utilisée pour toutes les tables.

Avec la stratégie IDENTITY, un AUTO_INCREMENT est ajouté à la colonne.

Gestion de auto_increment dans mysql

JPA et UUIDs

Un peu con comme implem : aucune mécanisme n'est prévu pour réconcilier deux instances...

Références

JPA and object heritage

08 May 2020

Cet exemple très complèt permet de comprendre les différentes stratégies de mapping Objet/relationnel dans le cas d'une hiérachie de classes.

Permet de voir les cas suivants :

  • Je ne parle pas du cas MappedSuperclass, que j'utilise déjà dans dicranum pour les kdm et cpl.
  • Table per class : la class mère est une entité, et chaque classe fille a sa propre table avec tous les champs. Permet de faire du polymporphisme, ou prix de requetes compliquées.
  • Single table : une seule table pour toute la hiérarchie. Un champ discriminant permet de distinguer les types, et les champs qui ne sont pas commun ne peut pas être NOT NULL.
  • Joined : un table pour les champs communs, et une table pour les champs supplémentaires de chaque classe fille.

Dovecot, Maildir et fichier eml

08 May 2020

https://doc.dovecot.org/configuration_manual/mail_location/
https://wiki.dovecot.org/MailboxFormat
http://blog.powered-up-games.com/wordpress/archives/51

Pour installer getmail_maildir :
apt-get install getmail4

https://linux.die.net/man/1/getmail_maildir

Spring boot authentication

22 April 2020

Le type JSON de mariadb apparait à partir de la version 10.2.7
https://mariadb.com/kb/en/json-data-type/

Attention, il n'est pas compatible avec la façon de faire de mysql.

Il est possible de passer un champ en JSON (alias de LONGTEXT, avec un check en entrée).

Ensuite, les constructions indiquées ici fonctionnent :
https://www.sitepoint.com/use-json-data-fields-mysql-databases/

CREATE TABLE book (
id mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
title varchar(200) NOT NULL,
tags json DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB;

INSERT INTO book (title, tags)
VALUES (
'ECMAScript 2015: A SitePoint Anthology',
'["JavaScript", "ES2015", "JSON"]'
);

-- all books with the 'JavaScript' tag:
SELECT * FROM book
WHERE JSON_CONTAINS(tags, '["JavaScript"]');

-- all books with tags starting 'Java':
SELECT * FROM book
WHERE JSON_SEARCH(tags, 'one', 'Java%') IS NOT NULL;

-- returns "SitePoint":
SELECT JSON_EXTRACT(
'{"id": 1, "website": "SitePoint"}',
'$.website'
);

JWT

06 April 2020

Spring boot authentication

01 April 2020

Exemple complètement basique

Une configuration par défaut peut être mise en place en ajoutant :

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

dans les logs.

Cette configuration peut soit être désactivée, soit "surpassée" en redéfinissant certaines méthodes de l'adapter.

A noter que la configuration a été simplifiée dans spring boot 2 et que la prise en compte de certaines propriétés à disparue.

Le chapitre suivant complète utilement, graçe à la syntaxe à utiliser dans la configuration de l'objet http :
https://www.baeldung.com/java-config-spring-security

httpBasic() genère un ensemble d'éléments de configuration basique
loginForm() genère une page de login en /login. Si un emplacement et précisé, avec loginPage("/login"), l'emplacement est appelé.

Quelques détails sur la gestion du formulaire / emplacement par défaut :
* Spring Security Form Login

Exemples d'authentification avec JWT avec Sprint Boot

Cet article montre un exemple interessant :

Autre exemple, envoyé par Marc-Antoine :

J'ai également trouvé des indications interessante dans le suivant :

Récupération des informations de l'utilisateur

Autres articles

https://stormpath.com/blog/build-secure-user-interfaces-using-jwts

https://openclassrooms.com/fr/courses/2091901-protegez-vous-efficacement-contre-les-failles-web/2863569-la-csrf

Très pédagogique :
https://hasura.io/blog/best-practices-of-using-jwt-with-graphql

https://www.baeldung.com/rest-api-spring-oauth2-angular
https://www.baeldung.com/keycloak-embedded-in-spring-boot-app
https://www.baeldung.com/spring-security-oauth-jwt

https://spring.io/guides/gs/securing-web/
https://dzone.com/articles/spring-boot-security-json-web-tokenjwt-hello-world

Problème particulier du hachage de mot de passe

https://www.baeldung.com/spring-security-5-default-password-encoder
https://www.devglan.com/spring-security/spring-boot-security-password-encoding-bcrypt-encoder
https://stackoverflow.com/questions/49633672/spring-security-5-0-0-removed-md5passwordencoder

Sur le traitement @preFilter ou @postFilter

Le français est bizarre, on dirait de la traduction automatique, mais permet de comprendre les annotations de méthodes.

Notes sur les tasklist sous Windows 10

31 March 2020

Git describe dans les projets Qt

09 March 2020

Git Patch

09 March 2020

Comment reproduire un changement dans un commit d'une branche dans une autre ?

Génération d'un patch

Dans la branche ou l'on veut noter les changements appliqués dans le dernier commit :

git format-patch -1 HEAD

Un fichier est créé :

0001-empty-user-barcode-fix.patch

0001 est un numéro attribué par la commande, empty-user-barcode-fix est le nom de la branche.

On obtient le patch suivant :

From c4719964670bbf01a227c3be3e071528a17cd96a Mon Sep 17 00:00:00 2001
From: Hans-Nikolas Locher <hnlocher@cst.fr>
Date: Thu, 26 Mar 2020 11:55:01 +0100
Subject: [PATCH] empty user barcode fix

---
 src/main/java/struts/action/UserAction.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/struts/action/UserAction.java b/src/main/java/struts/action/UserAction.java
index 82c5ff3..97bfa1f 100644
--- a/src/main/java/struts/action/UserAction.java
+++ b/src/main/java/struts/action/UserAction.java
@@ -79,7 +79,7 @@ public class UserAction extends DispatchAction {
 			for (User u:userForm.getUsers()){
 				if(u.getIduser()==idUser){
 
-					String codeBarreUser=u.getBarcodeBadge().toUpperCase().trim();
+					String codeBarreUser = (u.getBarcodeBadge() != null )?u.getBarcodeBadge().toUpperCase().trim():"";
 
 					if(droitModif(request.getUserPrincipal().getName(), u.getRole())){
 						userForm.setNom(u.getLastName());
-- 
2.9.0.windows.1

Application du patch sur une autre branche

On peut désormais se déplacer sur l'autre branche :

git checkout my-other-branch

Si l'on applique sans l'option -p1, patch cherche le fichier :

$ patch -p0 -i 0001-empty-user-barcode-fix.patch                                                    can't find file to patch at input line 14
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|From c4719964670bbf01a227c3be3e071528a17cd96a Mon Sep 17 00:00:00 2001
|From: Hans-Nikolas Locher <hnlocher@cst.fr>
|Date: Thu, 26 Mar 2020 11:55:01 +0100
|Subject: [PATCH] empty user barcode fix
|
|---
| src/main/java/struts/action/UserAction.java | 2 +-
| 1 file changed, 1 insertion(+), 1 deletion(-)
|
|diff --git a/src/main/java/struts/action/UserAction.java b/src/main/java/struts/action/UserAction.java
|index 82c5ff3..97bfa1f 100644
|--- a/src/main/java/struts/action/UserAction.java
|+++ b/src/main/java/struts/action/UserAction.java
--------------------------
File to patch:
[CTRL]+[C]

Donc en faisant :

patch -p1 -i 0001-empty-user-barcode-fix.patch

J'obtient dans un premier temps :

UserAction.java.rej

qui contient :

--- src/main/java/struts/action/UserAction.java
+++ src/main/java/struts/action/UserAction.java
@@ -79,7 +79,7 @@ public class UserAction extends DispatchAction {
 			for (User u:userForm.getUsers()){
 				if(u.getIduser()==idUser){
 
-					String codeBarreUser=u.getBarcodeBadge().toUpperCase().trim();
+					String codeBarreUser = (u.getBarcodeBadge() != null )?u.getBarcodeBadge().toUpperCase().trim():"";
 
 					if(droitModif(request.getUserPrincipal().getName(), u.getRole())){
 						userForm.setNom(u.getLastName());

C'est à dire la "charge" du patch.

En mettant des fins de ligne windows dans le patch :

patch -p1 -i 0001-empty-user-barcode-fix.patch
(Stripping trailing CRs from patch; use --binary to disable.)
patching file src/main/java/struts/action/UserAction.java

Les fins de ligne ne sont pas préservées, les fins de ligne windows ([CR][LF]) sont remplaçées par des fins de lignes unix ([LF]).

Enfin, si l'on change les fins de ligne du patch le patch en CR/LF et que l'on ajoute l'option --binary:

$ patch -p1 --binary -i 0001-empty-user-barcode-fix.patch
patching file src/main/java/struts/action/UserAction.java

Permet d'avoir le patch appliqué et les CR/LF maintenus tels qu'ils étaient, et donc de ne pas polluer l'historique avec des changements non signifiants à toutes les lignes...

Conversion de format sous Linux

07 March 2020

https://wiki.iac.ethz.ch/IT/LinuxConvertFiles

RDF / OWL ou EbuCore

05 March 2020

Exemple de modèle RDF : les données de

Modelisation RDF avec Jena

Jena est un framework java pour manipuler rdf.

La page suivante permet de s'appropier l'API par l'exemple.

Les relations entre sujet, predicat et sujet, peut être exprimé sous forme de triplet, ou alors en RDF.

Les ontologies sont une description de classes entre elle, et peuvent décrire les relations entre les objets d'un domaine.

Elle permettent en particulier d'inférer des informations à partir des règles définies. Par exemple, si un objet à un ISBN, alors c'est un livre.

Visualisation d'ontologies RDF

OWL est une extension de RDF pour noter les ontologies.

Dublin core

Correspond à un jeu dans le style de Dublin core.

https://www.dublincore.org/resources/metadata-basics/

Standardisé, aujourd'hui.

https://www.iso.org/standard/71339.html

EBU-Core

Ebu-core peut-être représenté comme un schéma, ou comme une ontonlogie.
Ebu core :
https://tech.ebu.ch/docs/tech/tech3293.pdf

Schema :
http://www.ebu.ch/metadata/schemas/EBUCore/ebucore.xsd

Ontologies :
https://www.ebu.ch/metadata/ontologies/ebucore/ebucore.rdf
http://www.w3.org/ns/ma-ont.rdf

Divers

Séparer le stockage de la représentation du modèle.

Docker and make

18 February 2020

Lancement de la console docker

Dans la console docker-tools, on ne dispose pas de make

La console est lançée de la façon suivante :

"C:\Program Files\Git\bin\bash.exe" --login -i "C:\Program Files\Docker Toolbox\start.sh"

Dans la console bash, le PATH est le suivant :

$ echo $PATH 
/c/Users/hnl-bis/bin:/mingw64/bin:/usr/local/bin:/usr/bin:/bin:/c/Program Files/Docker Toolbox:/c/Users/hnl-bis/bin:/mingw64/bin:/usr/local/bin:/usr/bin:/bin:/mingw64/bin:usr/bin:/c/Users/hnl-bis/bin:/c/Program Files (x86)/Common Files/Oracle/Java/javapath:/c/Program Files (x86)/Intel/iCLS Client:/c/Program Files (x86)/Python36-32/Scripts:/c/Program Files (x86)/Python36-32:/c/ProgramData/Oracle/Java/javapath:/c/Program Files/Intel/iCLS Client:/c/WINDOWS/system32:/c/WINDOWS:/c/WINDOWS/System32/Wbem:/c/WINDOWS/System32/WindowsPowerShell/v1.0:/c/Program Files/OpenVPN/bin:/c/Program Files/PuTTY:/c/Program Files (x86)/GnuPG/bin:/c/Program Files/nodejs:/c/Program Files (x86)/Intel/Intel(R) Management Engine Components/DAL:/c/Program Files/Intel/Intel(R) Management Engine Components/DAL:/c/Program Files (x86)/Intel/Intel(R) Management Engine Components/IPT:/c/Program Files/Intel/Intel(R) Management Engine Components/IPT:/c/WINDOWS/System32/OpenSSH:/c/Program Files (x86)/HashiCorp/Vagrant/bin:/cmd:/c/Program Files/erl10.3/bin:/c/Program Files (x86)/Elixir/bin:/c/WINDOWS/system32/config/systemprofile/.mix/escripts:/c/Program Files/MariaDB/MariaDB Connector C 64-bit/lib:/c/Program Files/MariaDB/MariaDB Connector C 64-bit/lib/plugin:/c/Users/hnl-bis/AppData/Local/Microsoft/WindowsApps:/c/Users/hnl-bis/AppData/Roaming/npm:/c/dev/liquibase:/c/dev/xampp/mysql/bin:/c/Program Files/gs/gs9.50/bin:/c/Program Files/gs/gs9.50/lib:/c/Users/hnl-bis/AppData/Local/Programs/Microsoft VS Code/bin:/usr/bin/vendor_perl:/usr/bin/core_perl:/usr/bin/vendor_perl:/usr/bin/core_perl

Pour pouvoir utiliser mingw32-make depuis la fenetre docker :

J'ajoute un .bashrc dasn ~hnl-bis contenant :

PATH=$PATH:/c/dev/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/bin

Du coup, au lancemenent suivant de la console creation d'un ~/.bash_profile contenant :

# generated by Git for Windows
test -f ~/.profile && . ~/.profile
test -f ~/.bashrc && . ~/.bashrc

Permet d'avoir mingw32-make dans la console bash de docker-tools.

Utilisation d'un Sonoff avec le firmware Tasmota

16 February 2020

Tout est parti d'une vidéo des frères Poulains qui proposent de flasher un module Sonoff avec un firmware Tasmota.

Comme lors de l'investigation précédente, la vidéo n'est pas tout à fait adaptée pour moi, il manque certains détails, comme la documentation des cablages, ou des solutions pour d'autres environnements, etc.
Par contre, la partie pédagogique ne m'est pas très utile, même s'il est très bien qu'elle y soit...

Bref.

Flashage d'un module Sonoff

J'ai commandé un barette de pins avec pas de 2,54 pour souder sur la carte.

Pour faire une barette de 4 éléments, j'ai du couper au nivau du 5e élément, il y a donc toujours un peu de perte.

Pas de pbm pour la soudure.

Pour le cablage, un peu plus compliqué, j'avais un doute sur l'utilisation de mon module USB, en particulier de savoir le VCC était bien en 3.3V, et non en 5V. Ensuite, j'ai du trouver le brochage.

Pour flasher, la vidéo donnait des indications pour windows, avec en particulier
(TODO).

J'ai de mon coté utilisé tasmotizer, que j'ai installé de la façon suivante :

  • Installation de python3-pip
sudo apt-get install python3-pip
python3 -m pip install pip --upgrade --user
python3 -m pip install pipenv --user
export PATH=$PATH:/home/hnl/.local/bin
pipenv
pip3 install tasmotizer

Ensuite

./tasmotizer.py

Firmware :
* Page : https://github.com/arendst/Tasmota/releases
* Firmware : https://github.com/arendst/Tasmota/releases/download/v8.1.0/tasmota-FR.bin

Je coche dans un premier temps de faire un backup du firmware Sonoff.

Il faut appuyer sur le bouton au moment d'insérer le module USB, pour être en mode boot.

Une fois le backup terminé, l'appareil ressort du mode boot, et il faut recommencer pour flasher.

J'ai ensuite utilisé le bouton "send config", mais sans succès.

Avec le firmware tasmota, le module passe en fall back sur un mode acccess point, on l'on peut configurer le SSID/mdp depuis la page de portail captif.

Notes couleurs bash

13 February 2020

Couleurs dans bash

https://misc.flogisoft.com/bash/tip_colors_and_formatting

Déploiement d'application Qt

13 February 2020

Récupération des dépendances

La ressource suivante fait le point sur les possibilités pour Qt5 de déployer une application :

L'application QTDIR/bin/windeployqt, permet d'enrichir un executable des dépendances Qt dont il a besoin.

Dans la cas de PlayerDialog ou je le teste, cela fonctionne, mais les bibliothèques tièrces ne sont pas ajoutées (kdsoap, libmysql...).

make install

Lecture de fichiers *.pri

  • qmake Language

  • To obtain the contents of an environment value when qmake is run, use the $$(...) operator.

  • The special $$[...] operator can be used to access qmake properties.
  • $$VARIABLE permet d'avoir le contenu d'une variable, equivalent à $${VARIABLE}

Compilation du plugin msyql pour QT5

Récupération des source de Qt :

git clone git://code.qt.io/qt/qt5.git
cd qt5
git checkout v5.14.1 # tag correspondant à la version

ajouter perl dans le path

configuration de la compilation des sources depuis la racine.

https://wiki.qt.io/Building_Qt_5_from_Git#Windows

perl init-repository # initialise les submodules

Configuration régulière :

> configure -opensource -confirm-license
> mingw32-make

Sinon, pour les modules mysql, je trouve plusieurs recettes.

Doc Qt5

https://doc.qt.io/qt-5/sql-driver.html#qmysql

> configure -opensource -confirm-license -sql-mysql
[...]
ERROR: Feature 'sql-mysql' was enabled, but the pre-condition 'libs.mysql' failed.

Check config.log for details.
[...]

configure -opensource -confirm-license -sql-mysql MYSQL_PREFIX="C:\dev\MySQL\MySQLConnectorC++8.0"

Il faut penser à supprimer config.log, config.cache et config.opt à chaque essai.

Pour mariadb, il a fallu renommer les libs pour quelle s'appellent mysql au lieu de mariadb.

Permet de compiler la module.

Ancienne méthode

L'ancienne méthode consistait à relocaliser les symboles de la dll de mysql, pour qu'elle fonctionne avec mingw.
https://qt.developpez.com/tutoriels/qtsql/compilation-pilotes/qmysql-mingw/
https://sourceforge.net/projects/mingw/files/MinGW/Extension/mingw-utils/

Installation de Cacti

09 February 2020

Installation Cacti

http://www.greatwhitewifi.com/2016/02/12/network-monitoring-with-raspberry-pi-part-1-cacti/

apt-get install cacti

Choix de la reconfiguration de apache2 comme serveur web.

L'installation est lourde de dépendances.

Il faut configurer une base de données.

base cacti
mdp cacti

user admin
mdp cacti

On peut créer un device avec l'adresse locale (127.0.0.1), mais

Il manquait snmpd

 $ sudo apt-get install snmp snmpd
Lecture des listes de paquets... Fait
Construction de l'arbre des dépendances
Lecture des informations d'état... Fait
snmp est déjà la version la plus récente (5.7.3+dfsg-5+b1).
snmp passé en « installé manuellement ».
Les paquets suivants ont été installés automatiquement et ne sont plus nécessaires :
  libexporter-tiny-perl liblist-moreutils-perl libregexp-assemble-perl
  point-rpi
Veuillez utiliser « sudo apt autoremove » pour les supprimer.
Paquets suggérés :
  snmptrapd
Les NOUVEAUX paquets suivants seront installés :
  snmpd
0 mis à jour, 1 nouvellement installés, 0 à enlever et 132 non mis à jour.
Il est nécessaire de prendre 55,9 ko dans les archives.
Après cette opération, 134 ko d'espace disque supplémentaires seront utilisés.
Souhaitez-vous continuer ? [O/n]
Réception de :1 http://mirrors.ircam.fr/pub/raspbian/raspbian buster/main armhf snmpd armhf 5.7.3+dfsg-5+b1 [55,9 kB]
55,9 ko réceptionnés en 0s (140 ko/s)
Préconfiguration des paquets...
Sélection du paquet snmpd précédemment désélectionné.
(Lecture de la base de données... 191083 fichiers et répertoires déjà installés.)
Préparation du dépaquetage de .../snmpd_5.7.3+dfsg-5+b1_armhf.deb ...
Dépaquetage de snmpd (5.7.3+dfsg-5+b1) ...
Paramétrage de snmpd (5.7.3+dfsg-5+b1) ...
adduser : Attention ! Le répertoire personnel « /var/lib/snmp » n'appartient pas à l'utilisateur que vous êtes en train de créer.
Created symlink /etc/systemd/system/multi-user.target.wants/snmpd.service → /lib/systemd/system/snmpd.service.
Traitement des actions différées (« triggers ») pour systemd (241-7~deb10u2+rpi1) ...
Traitement des actions différées (« triggers ») pour man-db (2.8.5-2) ...
$ service snmpd start

D'après
https://www.sugarbug.fr/framboise/full_raspcentreon3/install_snmp_raspcentreon/

Installation des mibs

sudo apt-get install snmp-mibs-downloader -y
sudo ln -s /usr/share/mibs/ /usr/share/snmp/mibs

Modification des fichiers /etc/default/snmp et /etc/snmp/snmpd.conf

Pour ajouter la mesure de temperature

https://www.onestep2.at/en/article/remote-sensor-raspberry-pi-locale-temperature-monitoring-snmp-and-cacti

Création d'un fichier de script pour récupérer la température. Pour pouvoir lire la temperature, le script ne peut pas être Debian-snmp.

sudo vi /etc/sudoers.d/temperature_snmp.hnl
sudo cat /etc/sudoers.d/temperature_snmp.hnl
Debian-snmp ALL=NOPASSWD:/opt/hnl/snmp/temp_cacti.sh
sudo chmod 440 /etc/sudoers.d/temperature_snmp.hnl
sudo mv /etc/sudoers.d/temperature_snmp.hnl /etc/sudoers.d/20-temperature_snmp_hnl

https://doc.ubuntu-fr.org/sudoers

Bon, cette première partie de la manip fonctionne.

Ensuite, il faut récupérer la donnée dans cacti, et c'est la que ça se complique un peu.

La manip - crade - du tuto consiste à faire un second script qui appelle snmpget et extrait la valeur.

Il faut ensuite créer une data source methode, puis un data source template, puis un graph template, enfin ajouter le graphe.

Référence

https://blog.cedrictemple.net/323-configuration-de-base-de-snmpd/

[DRAFT] Utilisation Sonoff bare

09 February 2020

La mac adresse du sonoff configuré avec le logiciel par défaut est :
83649010AA

extraction de fichier avec historique vers un nouveau repo

02 February 2020

cd temp
git clone ../../workspace/canhelp5-2016/ partial
cd partial
git remote remove origin
git subtree split -P doc -b keepdoc
git checkout keepdoc
# Il manque de quoi supprimer l'historique des fichiers qu'on ne veut pas. 
git filter-branch -f --index-filter 'git rm -r --cached --ignore-unmatch Erreur500ServiceMIF' HEAD
git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch *.{uml,patch,xml,txt,md}' HEAD
git filter-branch --prune-empty --tag-name-filter cat -- --all

Ensuite dans l'autre repo

git add partial ../partial
git fetch partial keepdoc
git checkout -b mergepartial
merge partial/keepdoc --allow-unrelated-histories

Une branche avec tous les mouvements

https://stackoverflow.com/questions/359424/detach-move-subdirectory-into-separate-git-repository
https://stackoverflow.com/questions/1365541/how-to-move-files-from-one-git-repo-to-another-not-a-clone-preserving-history

Utilisation port 443 pour Gitlab

06 January 2020

Propagation d'un tunnel ssh sur toutes les interfaces

06 January 2020

Propagation d'un tunnel ssh sur toutes les interfaces, il faut préciser la bind_address :

ssh -L0.0.0.0:10000:127.0.0.1:10000 root@192.168.30.23
-L [bind_address:]port:host:hostport

Références :

Import unrelated history in git

06 January 2020

Trouvé le parcours suivant sur SO :

git remote add unrelated <url-of-other-repo>
git fetch unrelated <branch-in-other-repo>
mkdir -p <path/to/new/code>
touch <path/to/new/code/>temp
git add <path/to/new/code/>temp
git commit -m 'Bring in new directory'
git merge --no-ff unrelated/<branch-in-other-repo> -s recursive -Xsubtree=<path/to/new/code>
git rm <path/to/new/code/temp>
git commit --amend --no-edit
git remote remove unrelated 

Pour importer evaluation-liquibase dans la branche liquibase de sidtp-db

# executé dans MinGW, ce qui explique le chemin
git remote add unrelated /c/Users/hnl-bis/workspace/essaiLiquibase
git fetch unrelated master
git commit -m 'Preparing merge of essai-liquibase'
git merge --no-ff unrelated/master -s recursive --allow-unrelated-histories
git commit --amend --no-edit
git remote remove unrelated 

Références

Utilisation d'un ESP-1 commandé par Blynk

06 January 2020

Ces notes reprenent un tuto sur youtube :

En suivant le tuto vidéo des frères poulain, il manque qq informations.

J'ai commandé un module USB, qui permet de brancher l'ESP-01S directement en USB sur l'ordinateur. J'ai peu de documentation livrée avec cet élément, la doc est manquante.

Pour l'utilisation d'un hardware 8266 directement depuis l'IDE Arduino, il faut une petite mise à jour.

Téléchargement d'une version plus récente du logiciel arduino 1.8.11.

Ensuite, il faut suivre les instructions suivante pour installer le module hardware du 8266.

https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version

En particulier :

cd %USERPROFILE%\Documents\Arduino\
if not exist hardware mkdir hardware
cd hardware
if not exist esp8266com mkdir esp8266com
cd esp8266com
git clone https://github.com/esp8266/Arduino.git esp8266

Pour installer le pilote hardware.

Pour installer Blynk, on peut utiliser la fonction de gestion des librairies, et installer Blynk.

Configuration du programme Blynk, et compilation.

Echec car il manque des fichiers dans le plugin hardware du 8266

En effet, il fallait aussi faire :

cd esp8266
git submodule update --init

et

cd esp8266/tools
python3 get.py

Ce qui installe un mingw local et procède aux compilations adéquates.

Le plus dur est de brancher les pin 1 et 3 ensemble (cf ma numérotation ci-dessous), en enfonçant la carte, ce qui permet l'upload du programme (mode boot).


! !-!!- !
! !
! !
! !
! !
! 1 2 3 4 !
! . . . . !
! . . . . !
! 8 7 6 5 !

J'ai ensuite retiré le module usb de l'ordinateur, et enlevé l'ESP 8266.

J'ai ensuite fixé l'ESP-8266 sur le module relais avec lequel ESP a été livré.

Je n'ai pas de documentation pour le module relais si ce n'est les informations suivantes dans la page de description du produit amazon :

Description du produit
Taille:ESP-01S + Relais

Caractéristique:
Ce relais ESP-01 / 01S basé sur le module WIFI ESP-01 / 01S. Il est conçu pour la maison intelligente, Internet et d'autres projets de bricolage. Avec ce relais intelligent, il est facile de bricoler votre commutateur intelligent pour contrôler n'importe quel appareil par votre téléphone n'importe où.

Paramètre
1. Tension de fonctionnement: DC5V-9V
2. Courant de fonctionnement: ≥250mA
3. Distance Transmission: la distance maximum de 400m (environnement ouvert)
4. Load: 10A / 250VAC 10A / 30VDC, le relais tire 100.000 fois
5. Taille du produit: 37 * 25mm

Comment utiliser?
1.Téléchargez le firmware sur l'ESP-01 / 01S via un adaptateur USB-série. Le lien pour télécharger les fichiers: https://github.com/IOT-MCU/ESP-01S-Relay-v4.0
2. Branchez le module ESP-01S sur l'en-tête 2 * 4 broches après avoir téléchargé le code sur l'ESP-01 / 01S.
3. Connectez une alimentation CC 5V-9V au GND et au VCC.
4.Installez ESP8266_Controller.apk sur votre téléphone Android.
5. Connectez le réseau de téléphonie mobile au point d'accès envoyé par ESP-01 / 01S: ESP8266_XXX, le mot de passe est le suivant: 12345678
6.Définissez l'adresse IP, l'adresse IP par défaut est: 192.168.1.1
7.Cliquez sur GPIO 0 ON OFF pour contrôler.

Emballage inclus:
1 x ESP8266 Relais
1 x ESP8266 ESP-01S Module

Ensuite, le plus dur à été de trouver une alimentation 5V, finalement avec un cable USB.

Couleur des cables :
Pin 1 : +5V Rouge
Pin 2 : Data- Blanc
Pin 3 : Data+ Vert
Pin 4 : GND Noir

J'ai donc branché le cable noir sur l'entrée GND et le rouge sur l'autre et branché la prise USB.

Ensuite, la liaison à Blynk à marché du premier coup.

Java SOAP Fault problems

05 January 2020

Mon premier souci vient du fait que les fault coté serveur ne sont pas mappées sur des Exception que je puisse gérer.

Dans le code généré, une seule exception apparait dans le code de chaque service.

Par exemple dans

com.sun.xml.internal.ws.fault.ServerSOAPFaultException une exception est associée à la création d'un service, pour gérer les URL mal formées.

    private final static WebServiceException SESSIONMANAGEMENTV10_EXCEPTION;
[]
	

Cette exception est lançée à la création du service si l'url (ou le nom de fichier) n'existent pas.

Il n'y a aucune exception de générée dans le code du port.

L'exception déclenchée, n'est pas directement récupérable :
com.sun.xml.internal.ws.fault.ServerSOAPFaultException

Changement du tooling

Un des essais consistait à préciser dans le .pom quelles versions devaient être utilisées pour compiler le WSDL.

Ce post, en particulier, indiquait qu'il pouvait s'agir d'un problème de version de lib.

En particulier, pour le plugin jaxws-maven-plugin, on ajoute :

                <dependencies>
                    <!--https://mvnrepository.com/artifact/com.sun.xml.ws/jaxws-tools -->
                    <dependency>
                        <groupId>com.sun.xml.ws</groupId>
                        <artifactId>jaxws-tools</artifactId>
                        <version>2.2.10</version>
                        <type>pom</type>
                    </dependency>
                </dependencies>

Il s'ensuit un long tunning, le changement de version provocant des erreurs :

Lorsque l'on change pour 2.3.2, par exemple et que l'on appelle mvn package :

[INFO] Processing: file:/C:/Users/hnl-bis/workspace/DoremiSoapDialog/src/main/resources/wsdl/v1_0/SessionManagement.wsdl
[WARNING] Using platform encoding (UTF-8), build is platform dependent!
[INFO] jaxws:wsimport args: [-keep, -s, 'C:\Users\hnl-bis\workspace\DoremiSoapDialog\target\generated-sources\jaxws-wsimport', -d, 'C:\Users\hnl-bis\workspace\DoremiSoapDialog\target\classes', -Xnocompile, "file:/C:/Users/hnl-bis/workspace/DoremiSoapDialog/src/main/resources/wsdl/v1_0/SessionManagement.wsdl"]
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at org.codehaus.mojo.jaxws.Invoker.main(Invoker.java:72)
Caused by: java.lang.NoClassDefFoundError: com/sun/tools/xjc/api/util/ToolsJarNotFoundException
	at com.sun.tools.ws.wscompile.Options$Target.<clinit>(Options.java:180)
	at com.sun.tools.ws.wscompile.Options.<init>(Options.java:191)
	at com.sun.tools.ws.wscompile.WsimportOptions.<init>(WsimportOptions.java:86)
	at com.sun.tools.ws.wscompile.WsimportTool.<init>(WsimportTool.java:91)
	at com.sun.tools.ws.wscompile.WsimportTool.<init>(WsimportTool.java:94)
	... 5 more
Caused by: java.lang.ClassNotFoundException: com.sun.tools.xjc.api.util.ToolsJarNotFoundException
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 10 more

Finalement, le seul changement observé est que :

/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.2.10
 * Generated source version: 2.2
 * 
 */

devient :

/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.2.5-b05 
 * Generated source version: 2.2
 * 
 */

Ce qui n'a pas beaucoup d'intéret.

Endorsed libs

Une autre piste est qu'à partir de java 1.6, il faut parfois superposer des bibliothèques qui sont dans le JDK officiel, et qu'il faut donc les redéfinir.

Cette situation est annonçée comme devant disparaitre.

En maven, cela se fait en chargeant des jar dans un repertoire endorsed dans la cible target.

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.3</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/endorsed</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>javax.xml.bind</groupId>
                                    <artifactId>jaxb-api</artifactId>
                                    <version>2.3.1</version>
                                    <type>jar</type>
                                </artifactItem>
                                <artifactItem>
                                    <groupId>javax.xml.ws</groupId>
                                    <artifactId>jaxws-api</artifactId>
                                    <version>2.3.1</version>
                                    <type>jar</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

Mais cela ne résoud absolument rien...

WebServiceException et SOAPFaultException

Server authentication failure Doremi_SOAP_Server "
	try
	{
	  SessionManagementV10 sessionPort = recoverSessionManagementPort();
	  System.err.println("Going to login");
	  String sessionString = sessionPort.login(user, password);
	  this.sessionId = "ceciestuneconnerie";
	} catch (SOAPFaultException e)
	{
	  SOAPFault fault = e.getFault();
	  debugSoapFault(fault);
	  throw new NodeException(fault.getFaultString());
	} catch (WebServiceException e)
	{
	  e.printStackTrace();
	  throw new NodeException(e);
	}

Lorsque le password n'est pas bon :

  • FaultCode : Server
  • FaultString : authentication failure
  • FaultActor : Doremi_SOAP_Server

Lorsque le token ne correspond à rien

  • FaultCode : Server
  • FaultString : not authenticated
  • FaultActor : Doremi_SOAP_Server

Soap Handler

Il doit y avoir moyen de créer un soap handler.
http://www.mkyong.com/webservices/jax-ws/jax-ws-soap-handler-in-client-side/

Références

Analyse CLI

02 January 2020

Une première piste pour faire l'analyse de la ligne de commande est Apache Commons CLI, mais ne parrait pas très à jour et surtout en conflit avec Apache Commons CLI 2 qui n'a jamais vraiment vu le jour.

Une autre piste est picocli, qui parrait très complet.

PicoCLI

Utilisation de Pandoc pour convertir d'un fichier en markdown

01 January 2020

Utilisation régulière

J'ai fait rédiger à mon fils en Markdown, puis on a pu convertir en fichier LibreOffice de la manière suivante :

#!/bin/bash

now=$(date +%F-%Hh%M)
pandoc EbaucheRapportStage3e.txt -f markdown -s -t odt -o render/EbaucheRapportStage3e$now.odt

Par contre, j'ai essayé d'utiliser nativement un template, mais l'approche suivante ne fonctionne pas, le texte est ajouté de manière inadéquate (sous forme de texte en dur).

Reference doc

En suivant les instruction de

pandoc EbaucheRapportStage3e.txt  -t markdown --reference-doc=schoolreport.ott -t odt -o EbaucheRapportStage3e-template.odt

En fait, le problème doit venir du fait que le template trouvé n'a pas été généré par pandoc.

A creuser...

Liens sur docker

19 December 2019

https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
https://putaindecode.io/articles/les-dockerfiles/
https://docs.docker.com/docker-hub/official_images/
https://docs.docker.com/v17.09/engine/admin/volumes/volumes/
https://docs.docker.com/engine/reference/builder/
https://stackoverflow.com/questions/50333650/install-python-package-in-docker-file
https://thenewstack.io/docker-basics-how-to-share-data-between-a-docker-container-and-host/
https://docs.docker.com/storage/volumes/
https://help.github.com/en/github/managing-packages-with-github-packages/configuring-docker-for-use-with-github-packages

Comment pusher sur github sans le port 22 ouvert ?

12 December 2019

Pbm au boulot, le port 22 ne répond pas (il n'est pas fermé, ni en interne, ni officiellement en externe). Eric cherche avec orange pourquoi.

Beaucoup de réponses indiquent de modifier .ssh/config ou d'ajouter un proxy http dans la config générale de git, mais ce n'est pas très propre.

Packaging d'application avec Python

25 November 2019

Venant de l'univers java, et assez client d'avoir des applications organisées de suivant une convention facile à retrouver, je cherchais l'équivalent en python.

Il n'y a pas de choses aussi structurées que maven dans l'univers python, mais il existe des bonne pratiques et des framework structurant.

Cette page renvoie elle même à d'autres pages interessantes :

Du coté de la doc officielle :

Déduplication fichiers vidéos

24 August 2019

Mon père à fait numériser les films Super 8 de notre enfance. J'ai pu en récupérer une partie sur le site, mais je n'ai pas eu le temps de tout copier avant l'expiration des liens.

J'ai donc fait une copie des fichiers depuis le disque dur de mon père.

Problème : ils ne portent pas les mêmes noms !

Je vais désigner ci-desssous les fichiers obtenus par le site par copie site, et les autres par copie disques durs.

Approche md5

Dans chacun des répertoires :

md5sum * > CHECKSUM.md5

Pour connaitre les md5 communs :

comm -12 <(cat CHECKSUM.md5 |cut -d" " -f 1|sort) <(cat Super8/FAMILY\ MOVIE/CHECKSUM.md5 |cut -d" " -f 1|sort ) > CHECKSUM-dupliques.txt

for i in $(cat CHECKSUM-dupliques.txt ); do grep $i CHECKSUM.md5 ; done

En fait, tous les fichiers déjà téléchargés sont dupliqués par la copie disque dur.

fslint

fslint propose un mode graphique pour identifier les contenus identiques. A l'aide de md5 et sha1.

Il existe une option pour fusionner les fichiers, en transformant les copies en liens en dur.

Copie des fichiers Family Move sur mon disque dur

20 August 2019

Source :

D:\FAMILY MOVIE

Destination :

E:\Perso\FAMILY MOVIE

Un script pourrait donc être le suivant :

echo. |time |find "actuelle" >> "E:\Perso\FAMILY MOVIE\xcopy.log"
echo. |date |find "jour" >> "E:\Perso\FAMILY MOVIE\xcopy.log"
xcopy "D:\FAMILY MOVIE" "E:\Perso\FAMILY MOVIE" /E /R /I /Y >> "E:\Perso\FAMILY MOVIE\xcopy.log"
echo. |time |find "actuelle" >> "E:\Perso\FAMILY MOVIE\xcopy.log"
echo. |date |find "jour" >> "E:\Perso\FAMILY MOVIE\xcopy.log"

Emulation Arduino

18 August 2019

Un des problèmes pour avancer avec le développement de l'arduino, est la nécessisté d'avoir un board branché et alimenté pour faire des tests.

D'ou la recherche d'un émulateur. Je commence par une petite recherche google.

Le point de départ est :
* StackExchange : Can I program for Arduino without having a real board?

Le problèmes, est - comme d'habitude - que certains émulateurs ne fonctionnent que dans des environnements complèts, comme tinkercad, ou autre.

Un petit rappel terminologique pour distinguer un simulateur d'un émulateur.
* StackExchange : What's the difference between simulation and emulation

Fritzing

Fritzing est un logiciel de conception de circuit imprimé. Une légende veut qu'il y ait un émulateur arduino intégré, mais cela semble être la survivance d'un projet jamais abouti.

Virtualboard

C'est le nom qui revient le plus. Facile à installer, apparement, mais peut-être moins simple à faire fonctionner d'après certains commentaires.

Proteus

La disponibilité du logiciel n'est pas claire. Il semblerait que la version enregistrée ne permettent pas d'enregistrer les schémas élaborés.

https://www.labcenter.com/downloads/
https://labcenter.s3.amazonaws.com/downloads/Tutorials.pdf

Virtronics

Tinkercad

A vérifier, il semblerait que le virtronics soit un ancètre de Tinkercad.

Tinkercad demande une inscription.

Tinkercad permet bien d'autre modélisation, mais entre autre, la modélisation de circuits.

https://www.tinkercad.com/circuits

CodeBlocks

How to represent a file folder tree with dot ?

04 August 2019

Rendu d'arbres de fichiers

En vrac

Installation de docker sur Windows 10 avec VirtualBox

24 July 2019

Installation de docker tools

Docker ne fonctionne pas.

Docker Machine is not installed. Please re-run the Toolbox Installer and try again.
Looks like something went wrong in step ´Looking for vboxmanage.exe´... Press any key to continue...

Je désinstalle VirtualBox 5.2.12.

Je réinstalle dockertools avec l'option VirtualBox 5.2.20 et l'otion S6.

Toujours le même message en essayant de démarrer le backend avec l'icone

Essaye la chose suivante :
https://stackoverflow.com/questions/39373217/docker-looks-something-went-wrong-in-step-looking-for-vboxmanage-exe

Configurer DOCKER_MACHINE et DOCKER_TOOLBOX_INSTALL_PATH

DOCKER_MACHINE=C:\Program Files\Docker Toolbox\docker-machine.exe
DOCKER_TOOLBOX_INSTALL_PATH=C:\Program Files\Docker Toolbox

Résoud le problème d'affichage du message.

Ensuite, docker ne fonctionne pas (en tout cas pour le moment), depuis la console windows, mais uniquement depuis la console docker, qui initialise un environnement GNU avec minGW.

Ref

Notes sur l'API java cryptographic architecture

24 July 2019

Un certain nombre de choses sont possibles dans l'API java de base, JCA, et les classes de sécurité dans java.security.

Mais dès que l'on cherche des fonctions plus précises, en particuliers sur la manipulation de chaines ASN.1, et l'utilisation des certificats X509, on est très vite orienté vers la bibliothèque java Bouncy Castle.

L'ennui, lorsque l'on suit les exemples de codes, c'est qu'il y a beaucoup d'évolutions dans la bibliothèque Bouncy Castle, d'une version à l'autre, et que les API changent. Du coup les exemples sont durs à faire fonctionner pour certains (les plus précis comme sur les extensions X509).

Exemple d'évolution de l'API :

Par exemple, créer un certificat X509 en java sans Bouncy Castle semble être compliqué.

Bibliographie

JPA et les requêtes SQL libres

04 May 2019

Sur la page suivante, on voit comment faire des requêtes en SQL natif :

En gros, on procède pareil qu'en JPQL query.

Query q = em.createNativeQuery("SELECT a.firstname, a.lastname FROM Author a");
List<Object[]> authors = q.getResultList();
 
for (Object[] a : authors) {
    System.out.println("Author "
            + a[0]
            + " "
            + a[1]);
}

On peut même faire mieux, avec du binding de paramètres.

Mais arretons nous avant : comment on obtient l'entityManager ?

Trouvé sur StackOverflow :

@PersistenceContext
private EntityManager entityManager;

@Query

On peut également utiliser l'annotation @Query, comme expliqué ici :

Gestion attribut à valeur multiple

01 May 2019

Je cherche depuis quelques jours comment modéliser un lien de composition au sens de la modélisation objet, c'est à dire une agrégation vers des objets qui n'ont pas d'existence propre. L'exemple typique de ce type de construction, c'est une voiture et ses quatres roues. Les roues composent -entre autres- la voiture.

Dans le cas du développement de Dicranum, voici le problème que je dois modéliser :

Un Email à un attribut from, mais plusieurs attributs TO et CC.

A noter : pour simplifier la discussion ici, je ne prend pas exemple sur les assets liées à l'email, qui éventuellment pourrait devenir plus complexe (liaison manyToMany, etc).

Modélisation en base de données

J'ai donc créé une table Email de la façon suivante :

Nom Type Interclassement Attributs Null Valeur par défaut Commentaires Extra

1 id_emailPrimaire int(16) Non Aucune AUTO_INCREMENT
2 emailaccount varchar(255) utf8_general_ci Non Aucune
3 uidIndex varchar(255) utf8_general_ci Non Aucune
4 subject varchar(255) utf8_general_ci Non Aucune
5 sender varchar(255) utf8_general_ci Non Aucune
6 senddate datetime Non Aucune
7 emldirpath varchar(1024) utf8_general_ci Oui NULL
8 emlfilename varchar(255) utf8_general_ci Oui NULL

Mais aussi une autre table EmailParticipant pour les champs TO,CC

Nom Type Interclassement Attributs Null Valeur par défaut Commentaires Extra

1 email_idemailPrimaire int(11) Non Aucune
2 relationPrimaire enum('TO', 'CC') utf8_general_ci Non Aucune
3 adressPrimaire varchar(255) utf8_general_ci Non Aucune

Modélisation avec des Entitées

La première modélisation Objet fait de EmailParticipant une véritable entité avec une relation OneToMany.

package fr.cst.rd.digitalCinemaAssetHandler.entities;

[... Imports ...]

@Entity
@Table(name="email")
public class Email
{

	// id_email	int(16)	NO	PRI		auto_increment
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="id_email")
	@JsonIgnore
	int id_email;

	// emailaccount	varchar(255)	NO
	@Column(name="emailaccount")
	private String emailAccount;

	// uid	char(64)	NO	UNI
	@Column(name="uid")
	private String emailUid;

	// subject	varchar(255)	NO
	@Column(name="subject")
	private String subject;

	// sender	varchar(255)	NO
	@Column(name="sender")
	private String from;

	// senddate	datetime	NO
	@Column(name="senddate")
	private Date sendDate;

	// emldirpath	varchar(1024)	YES
	@Column(name="emldirpath")
	private String emldirpath;

	// emlfilename	varchar(255)	YES
	@Column(name="emlfilename")
	private String emlfilename;


	// participants
	@OneToMany(cascade = CascadeType.PERSIST, orphanRemoval = true, fetch = FetchType.EAGER)
	@JoinColumn(name = "email_idemail")
	private Set<EmailParticipant> participants = new HashSet<EmailParticipant>();


[... setters and getters ...]

	public void addToParticipant(String address) {
		EmailParticipant participant = new EmailParticipant();
		participant.setRelation("TO");
		participant.setAddress(address);
		participant.setEmail(this);
		this.participants.add(participant);
	}

	public void addCcParticipant(String address) {
		EmailParticipant participant = new EmailParticipant();
		participant.setRelation("CC");
		participant.setAddress(address);
		participant.setEmail(this);
		this.participants.add(participant);
	}


[...  ...]


}

Et la classe EmailParticipant :

package fr.cst.rd.digitalCinemaAssetHandler.entities;

[... imports ... ]

@Entity
@Table(name="emailparticipant")
@IdClass(EmailParticipant.class)
public class EmailParticipant implements Serializable
{
//		@JsonIgnore
//		@Id
//		@Column(name = "email_idemail")
//		private int email_idemail;

		@Id
		@Column(name = "relation")
		private String relation;

		@Id
		@Column(name = "adress")
		private String address;

		@JsonIgnore()
		@Id
		@ManyToOne(targetEntity=Email.class)
		@JoinColumn(name="email_idemail")
		Email email;

		public EmailParticipant() {
		}

		@JsonIgnore()
		public Integer getEmail_idemail() {
			// return email_idemail;
			return email.getId_email();
		}

[... setters and getters ...]


		// Identificateur doit implémenter serializable
		// https://vladmihalcea.com/the-best-way-to-map-a-composite-primary-key-with-jpa-and-hibernate/
		@Override
		public boolean equals(Object o) {
			if (this == o) return true;
			if (!(o instanceof EmailParticipant)) return false;
			EmailParticipant that = (EmailParticipant) o;
			return Objects.equals(getEmail_idemail(), that.getEmail_idemail()) &&
					Objects.equals(getAddress(), that.getAddress()) &&
					Objects.equals(getRelation(), that.getRelation());
		}

		@Override
		public int hashCode() {
			return Objects.hash(getEmail_idemail(),getRelation(), getAddress());
		}

}

Il y a plusieurs éléments de complexité :

  • La clef primaire de emailparticipation est (email_idemail,relation,address) soit l'ensembles des attributs. Il n'y a pas d'identifiant indépendant, j'y reviendrais plus bas.
  • La relation OneToMany n'est pas unidirectionnelle, j'ai du remplacer la champ email_idemail, par un objet dans l'entité avec une liaison ManyToOne vers Email (complètement inutile sémantiquement). Mais sans cette liaison inverse, les insertion en base de donnée ne se faisaient pas correctement, le champ email_idemail n'était pas initialisé correctement.
  • Il faut redéfinir la relation equals et hashCode et implémenter l'interface Serializable au sein de l'objet servant d'identifiant et comprenant l'ensemble des champs (ici la classe elle même).
  • L'Id n'est pas définit pas une autre classe Embeddable et un attribut de cette classe Embedded dans la classe elle même. Ici, c'est l'annotation IdClass qui pointe vers la classe elle même.

Clairement, un des problèmes vient de la clef composite comprenant la clef étrangère vers l'entité email.

Dans Wikibook il y à une page consacrée à JPA et l'utilisation de OneToMany :

The target object in a unidirectional OneToMany is an independent object, so it should not rely on the foreign key in any way, i.e. the foreign key cannot be part of its primary key, nor generally have a not null constraint on it.

Je pense que le problème rencontré vient de la : il ne faut pas utiliser la clef étrangère comme partie de la clef primaire de EmailParticipant.

Modélisation avec des @ElementCollection

La note de blog suivante fait état d'un point d'aancement vers JPA 2, qui autorise une nouvelle modélisation de collection. L'auteu mentionne un nom d'annotation qui n'est pas le nom définitif.

L'article suivant présente un cas d'usage très proche du mien avec un user avec de multiples numéros de téléphone.

Le résultat est parfait :

  • lecture ok
  • ecriture ok
  • ajout d'un element supplémentaire en cc ou to ok

Il à refalu définir la relation equals, en supprimant la notion d'email, pour avoir une identité lorsque même relation et même adresse.

Coté Email, la relation devient :

    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "emailparticipant", joinColumns = @JoinColumn(name = "email_idemail"))
    @AttributeOverrides({
            @AttributeOverride(name = "relation", column = @Column(name = "relation")),
            @AttributeOverride(name = "address", column = @Column(name = "adress"))
    })
    @Column(name = "adress")
	private Set<EmailParticipant> participants = new HashSet<EmailParticipant>();

Il n'y plus besoin d'ajouter le mail :

	public void addToParticipant(String address) {
		EmailParticipant participant = new EmailParticipant();
		participant.setRelation("TO");
		participant.setAddress(address);
		// participant.setEmail(this);
		this.participants.add(participant);
	}

Coté EmailParticipant :

// [... Imports ...]

@Embeddable
public class EmailParticipant implements Serializable
{

		@Column(name = "relation")
		private String relation;

		@Column(name = "adress")
		private String address;

		public EmailParticipant() {
		}

		public EmailParticipant(String relation, String address) {
			super();
			this.relation = relation;
			this.address = address;
		}

		// [... Getters et setters supprimés pour brièveté ...] 

		@Override
		public boolean equals(Object o) {
			if (this == o) return true;
			if (!(o instanceof EmailParticipant)) return false;
			EmailParticipant that = (EmailParticipant) o;
			return Objects.equals(getAddress(), that.getAddress()) &&
					Objects.equals(getRelation(), that.getRelation());
		}

		@Override
		public int hashCode() {
			return Objects.hash(getRelation(), getAddress());
		}
}

Gestion des les assets attachés à un mail

Sémantiquement, il s'agit d'une liaison ManyToMany avec une table d'association emailassetpresence.

Ce qui peut se gérer assez simplement.

MAIS, si l'on veut pouvoir ajouter des attributs à la liaison, il faut créer une entité et faire deux liaisons OneToMany.

EmailAssetPresence devient :

// Imports

@Entity
@Table(name="emailassetpresence")
public class EmailAssetPresence
{
	@Embeddable
	public static class EmailAssetPresenceId implements Serializable
	{


		private static final long serialVersionUID = 5066650226848630075L;

		@Column(name = "email_idemail")
		private Integer email_idemail;


		@Column(name = "assetuuid")
		private String assetUuid;


		public EmailAssetPresenceId() {
		}

		@Override
		public boolean equals(Object o) {
			if (this == o) return true;
			if (!(o instanceof EmailAssetPresence)) return false;
			EmailAssetPresenceId that = (EmailAssetPresenceId) o;
			return Objects.equals(getEmail_idemail(), that.getEmail_idemail()) &&
					Objects.equals(getAssetUuid(), that.getAssetUuid());
		}

		@Override
		public int hashCode() {
			return Objects.hash(getEmail_idemail(), getAssetUuid());
		}

	// Getters

	}

	@JsonIgnore
	@EmbeddedId
	private EmailAssetPresenceId emailAssetPresenceId;

	@ManyToOne
	@MapsId("id_email")
	@JoinColumn(name="email_idemail")
	private Email email;

	// ENUM('KDM','CPL','PKL')
	@Column(name = "assetkind")
	private String assetkind;

	@Column(name = "path")
	private String path;

	@Column(name = "filename")
	private String filename;

	//    insertTime // Faut-il inclure ?
	@Column(name = "creationtime", insertable = false, updatable = false)
	@Temporal(TemporalType.TIMESTAMP)
	private Date creationTime;

		public void setEmail(Email email) {
			this.email = email;
			this.emailAssetPresenceId.email_idemail = email.id_email;
		}
//
//		@JsonIgnore()
//		public Integer getEmail_idemail() {
//			return email.getId_email();
//		}

		public EmailAssetPresence()
		{
			this.emailAssetPresenceId = new EmailAssetPresenceId();
		}

// getters and setters


}

Coté Email :

	// assets
	@OneToMany(cascade = CascadeType.PERSIST, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy="email")
	// @JoinColumn(name = "email_idemail")
	private Set<EmailAssetPresence> assetList = new HashSet<EmailAssetPresence>();

=> On retombe sur les problèmes précédents de rafraichissement des clef etrangères.

Il me semble par contre qu'il y a quelques indications de syntaxe interessant par rapport à la litterature précédément rencontrée...

Autres liens

Piste externalisation configuration externe pour Spring Boot

29 April 2019

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html

Notation des grammaires formelles

29 April 2019

Très bonne formalisation des différentes syntaxe pour définir des grammaires sur le site de :
* lmu : Syntax

Format railroad :
Editeur de schémas railroad à partir d'une grammaire EBNF.

EBNF for email description

Une grammaire de l'organisation d'un email, en fonction des RFC est donnée ici, mais le formalisme est étrange.

Grammaire originale :

1. NUL = "ASCII code zero" ;
2. HTAB = "ASCII code 9" ;
3. LF = "ASCII code 10" ;
4. CR = "ASCII code 13" ;
5. CRLF = CR4 LF3 ;
6. SP = "ASCII code 32" ;
7. WSP = SP6 | HTAB2 ;
8. ALPHA = "A".."Z" | "a".."z" ;
9. DIGIT = "0".."9" ;
10. DQUOTE = "\"" ;
11. VCHAR = "!".."~" ;
12. quoted_pair = "\\" ( VCHAR11 | WSP7 ) | obs_qp96 ;
13. FWS = [ { WSP7 } CRLF5 ] WSP7 { WSP7 } | obs_FWS101 ;
14. ctext = "!".."'" | "*".."[" | "]".."~" | obs_ctext93 ;
15. ccontent = ctext14 | quoted_pair12 | comment16 ;
16. comment = "(" { [ FWS13 ] ccontent15 } [ FWS13 ] ")" ;
17. CFWS = [ FWS13 ] comment16 { [ FWS13 ] comment16 } [ FWS13 ] | FWS13 ;
18. atext = ALPHA8 | DIGIT9 | "!" | "#" | "$" | "%" | "&" | "'" | "*" | "+" | "-" | "/" | "=" | "?" | "^" | "_" | "`" | "{" | "|" | "}" | "~" ;
19. atom = [ CFWS17 ] atext18 { atext18 } [ CFWS17 ] ;
20. dot_atom_text = atext18 { atext18 } { "." atext18 { atext18 } } ;
21. dot_atom = [ CFWS17 ] dot_atom_text20 [ CFWS17 ] ;
22. specials = "(" | ")" | "<" | ">" | "[" | "]" | ":" | ";" | "@" | "\\" | "," | "." | DQUOTE10 ;
23. qtext = "!" | "#".."[" | "]".."~" | obs_qtext94 ;
24. qcontent = qtext23 | quoted_pair12 ;
25. quoted_string = [ CFWS17 ] DQUOTE10 { [ FWS13 ] qcontent24 } [ FWS13 ] DQUOTE10 [ CFWS17 ] ;
26. word = atom19 | quoted_string25 ;
27. phrase = word26 { word26 } | obs_phrase99 ;
28. unstructured = { [ FWS13 ] VCHAR11 } { WSP7 } | obs_unstruct98 ;
29. date_time = [ day_of_week30 "," ] date32 time36 [ CFWS17 ] ;
30. day_of_week = [ FWS13 ] day_name31 | obs_day_of_week102 ;
31. day_name = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun" ;
32. date = day33 month34 year35 ;
33. day = [ FWS13 ] DIGIT9 [ DIGIT9 ] FWS13 | obs_day103 ;
34. month = "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec" ;
35. year = FWS13 DIGIT9 DIGIT9 DIGIT9 DIGIT9 FWS13 | obs_year104 ;
36. time = time_of_day37 zone41 ;
37. time_of_day = hour38 ":" minute39 [ ":" second40 ] ;
38. hour = DIGIT9 DIGIT9 | obs_hour105 ;
39. minute = DIGIT9 DIGIT9 | obs_minute106 ;
40. second = DIGIT9 DIGIT9 | obs_second107 ;
41. zone = FWS13 ( "+" | "-" ) DIGIT9 DIGIT9 DIGIT9 DIGIT9 | obs_zone108 ;
42. address = mailbox43 | group46 ;
43. mailbox = name_addr44 | addr_spec51 ;
44. name_addr = [ display_name47 ] angle_addr45 ;
45. angle_addr = [ CFWS17 ] "<" addr_spec51 ">" [ CFWS17 ] | obs_angle_addr109 ;
46. group = display_name47 ":" [ group_list50 ] ";" [ CFWS17 ] ;
47. display_name = phrase27 ;
48. mailbox_list = mailbox43 { "," mailbox43 } | obs_mbox_list112 ;
49. address_list = address42 { "," address42 } | obs_addr_list113 ;
50. group_list = mailbox_list48 | CFWS17 | obs_group_list114 ;
51. addr_spec = local_part52 "@" domain53 ;
52. local_part = dot_atom21 | quoted_string25 | obs_local_part115 ;
53. domain = dot_atom21 | domain_literal54 | obs_domain116 ;
54. domain_literal = [ CFWS17 ] "[" { [ FWS13 ] dtext55 } [ FWS13 ] "]" [ CFWS17 ] ;
55. dtext = "!".."Z" | "^".."~" | obs_dtext117 ;
56. message = ( fields59 | obs_fields118 ) [ CRLF5 body57 ] ;
57. body = { { text58 } CRLF5 } { text58 } | obs_body97 ;
58. text = "ASCII codes: 1-9, 11, 12, 14-127" ;
59. fields = { trace84 { optional_field89 } | { resent_date77 | resent_from78 | resent_sender79 | resent_to80 | resent_cc81 | resent_bcc82 | resent_msg_id83 } } { orig_date60 | from61 | sender62 | reply_to63 | to64 | cc65 | bcc66 | message_id67 | in_reply_to68 | references69 | subject74 | comments75 | keywords76 | optional_field89 } ;
60. orig_date = "Date:" date_time29 CRLF5 ;
61. from = "From:" mailbox_list48 CRLF5 ;
62. sender = "Sender:" mailbox43 CRLF5 ;
63. reply_to = "Reply_To:" address_list49 CRLF5 ;
64. to = "To:" address_list49 CRLF5 ;
65. cc = "Cc:" address_list49 CRLF5 ;
66. bcc = "Bcc:" [ address_list49 | CFWS17 ] CRLF5 ;
67. message_id = "Message_ID:" msg_id70 CRLF5 ;
68. in_reply_to = "In_Reply_To:" msg_id70 { msg_id70 } CRLF5 ;
69. references = "References:" msg_id70 { msg_id70 } CRLF5 ;
70. msg_id = [ CFWS17 ] "<" id_left71 "@" id_right72 ">" [ CFWS17 ] ;
71. id_left = dot_atom_text20 | obs_id_left129 ;
72. id_right = dot_atom_text20 | no_fold_literal73 | obs_id_right130 ;
73. no_fold_literal = "[" { dtext55 } "]" ;
74. subject = "Subject:" unstructured28 CRLF5 ;
75. comments = "Comments:" unstructured28 CRLF5 ;
76. keywords = "Keywords:" phrase27 { "," phrase27 } CRLF5 ;
77. resent_date = "Resent_Date:" date_time29 CRLF5 ;
78. resent_from = "Resent_From:" mailbox_list48 CRLF5 ;
79. resent_sender = "Resent_Sender:" mailbox43 CRLF5 ;
80. resent_to = "Resent_To:" address_list49 CRLF5 ;
81. resent_cc = "Resent_Cc:" address_list49 CRLF5 ;
82. resent_bcc = "Resent_Bcc:" [ address_list49 | CFWS17 ] CRLF5 ;
83. resent_msg_id = "Resent_Message_ID:" msg_id70 CRLF5 ;
84. trace = [ return85 ] received87 { received87 } ;
85. return = "Return_Path:" path86 CRLF5 ;
86. path = angle_addr45 | [ CFWS17 ] "<" [ CFWS17 ] ">" [ CFWS17 ] ;
87. received = "Received:" { received_token88 } ";" date_time29 CRLF5 ;
88. received_token = word26 | angle_addr45 | addr_spec51 | domain53 ;
89. optional_field = field_name90 ":" unstructured28 CRLF5 ;
90. field_name = ftext91 { ftext91 } ;
91. ftext = "!".."9" | ";".."~" ;
92. obs_NO_WS_CTL = "ASCII codes 1-8, 11, 12, 14-31, 127" ;
93. obs_ctext = obs_NO_WS_CTL92 ;
94. obs_qtext = obs_NO_WS_CTL92 ;
95. obs_utext = NUL1 | obs_NO_WS_CTL92 | VCHAR11 ;
96. obs_qp = "\\" ( NUL1 | obs_NO_WS_CTL92 | LF3 | CR4 ) ;
97. obs_body = { { LF3 } { CR4 } { ( NUL1 | text58 ) { LF3 } { CR4 } } | CRLF5 } ;
98. obs_unstruct = { { LF3 } { CR4 } { obs_utext95 { LF3 } { CR4 } } | FWS13 } ;
99. obs_phrase = word26 { word26 | "." | CFWS17 } ;
100. obs_phrase_list = [ phrase27 | CFWS17 ] { "," [ phrase27 | CFWS17 ] } ;
101. obs_FWS = WSP7 { WSP7 } { CRLF5 WSP7 { WSP7 } } ;
102. obs_day_of_week = [ CFWS17 ] day_name31 [ CFWS17 ] ;
103. obs_day = [ CFWS17 ] DIGIT9 [ DIGIT9 ] [ CFWS17 ] ;
104. obs_year = [ CFWS17 ] DIGIT9 DIGIT9 { DIGIT9 } [ CFWS17 ] ;
105. obs_hour = [ CFWS17 ] DIGIT9 DIGIT9 [ CFWS17 ] ;
106. obs_minute = [ CFWS17 ] DIGIT9 DIGIT9 [ CFWS17 ] ;
107. obs_second = [ CFWS17 ] DIGIT9 DIGIT9 [ CFWS17 ] ;
108. obs_zone = "UT" | "GMT" | "EST" | "EDT" | "CST" | "CDT" | "MST" | "MDT" | "PST" | "PDT" | "A".."I" | "K".."Z" | "a".."i" | "k".."z" ;
109. obs_angle_addr = [ CFWS17 ] "<" obs_route110 addr_spec51 ">" [ CFWS17 ] ;
110. obs_route = obs_domain_list111 ":" ;
111. obs_domain_list = { CFWS17 | "," } "@" domain53 { "," [ CFWS17 ] [ "@" domain53 ] } ;
112. obs_mbox_list = { [ CFWS17 ] "," } mailbox43 { "," [ mailbox43 | CFWS17 ] } ;
113. obs_addr_list = { [ CFWS17 ] "," } address42 { "," [ address42 | CFWS17 ] } ;
114. obs_group_list = [ CFWS17 ] "," { [ CFWS17 ] "," } [ CFWS17 ] ;
115. obs_local_part = word26 { "." word26 } ;
116. obs_domain = atom19 { "." atom19 } ;
117. obs_dtext = obs_NO_WS_CTL92 | quoted_pair12 ;
118. obs_fields = { obs_return142 | obs_received143 | obs_orig_date119 | obs_from120 | obs_sender121 | obs_reply_to122 | obs_to123 | obs_cc124 | obs_bcc125 | obs_message_id126 | obs_in_reply_to127 | obs_references128 | obs_subject131 | obs_comments132 | obs_keywords133 | obs_resent_date136 | obs_resent_from134 | obs_resent_send135 | obs_resent_rply141 | obs_resent_to137 | obs_resent_cc138 | obs_resent_bcc139 | obs_resent_mid140 | obs_optional144 } ;
119. obs_orig_date = "Date" { WSP7 } ":" date_time29 CRLF5 ;
120. obs_from = "From" { WSP7 } ":" mailbox_list48 CRLF5 ;
121. obs_sender = "Sender" { WSP7 } ":" mailbox43 CRLF5 ;
122. obs_reply_to = "Reply_To" { WSP7 } ":" address_list49 CRLF5 ;
123. obs_to = "To" { WSP7 } ":" address_list49 CRLF5 ;
124. obs_cc = "Cc" { WSP7 } ":" address_list49 CRLF5 ;
125. obs_bcc = "Bcc" { WSP7 } ":" ( address_list49 | { [ CFWS17 ] "," } [ CFWS17 ] ) CRLF5 ;
126. obs_message_id = "Message_ID" { WSP7 } ":" msg_id70 CRLF5 ;
127. obs_in_reply_to = "In_Reply_To" { WSP7 } ":" { phrase27 | msg_id70 } CRLF5 ;
128. obs_references = "References" { WSP7 } ":" { phrase27 | msg_id70 } CRLF5 ;
129. obs_id_left = local_part52 ;
130. obs_id_right = domain53 ;
131. obs_subject = "Subject" { WSP7 } ":" unstructured28 CRLF5 ;
132. obs_comments = "Comments" { WSP7 } ":" unstructured28 CRLF5 ;
133. obs_keywords = "Keywords" { WSP7 } ":" obs_phrase_list100 CRLF5 ;
134. obs_resent_from = "Resent_From" { WSP7 } ":" mailbox_list48 CRLF5 ;
135. obs_resent_send = "Resent_Sender" { WSP7 } ":" mailbox43 CRLF5 ;
136. obs_resent_date = "Resent_Date" { WSP7 } ":" date_time29 CRLF5 ;
137. obs_resent_to = "Resent_To" { WSP7 } ":" address_list49 CRLF5 ;
138. obs_resent_cc = "Resent_Cc" { WSP7 } ":" address_list49 CRLF5 ;
139. obs_resent_bcc = "Resent_Bcc" { WSP7 } ":" ( address_list49 | { [ CFWS17 ] "," } [ CFWS17 ] ) CRLF5 ;
140. obs_resent_mid = "Resent_Message_ID" { WSP7 } ":" msg_id70 CRLF5 ;
141. obs_resent_rply = "Resent_Reply_To" { WSP7 } ":" address_list49 CRLF5 ;
142. obs_return = "Return_Path" { WSP7 } ":" path86 CRLF5 ;
143. obs_received = "Received" { WSP7 } ":" { received_token88 } CRLF5 ;
144. obs_optional = field_name90 { WSP7 } ":" unstructured28 CRLF5 ;

Avec quelques transformations pour que cela ressemble à une grammaire EBNF
(Il reste des problèmes, en particulier pour les ranges de caractères, notés '..' .
En particulier, cela ne fonctionne pas avec le site de railroad ci dessus.

 NUL ::= "ASCII code zero" ;
 HTAB ::= "ASCII code 9" ;
 LF ::= "ASCII code 10" ;
 CR ::= "ASCII code 13" ;
 CRLF ::= CR4 LF3 ;
 SP ::= "ASCII code 32" ;
 WSP ::= SP6 | HTAB2 ;
 ALPHA ::= "A".."Z" | "a".."z" ;
 DIGIT ::= "0".."9" ;
 DQUOTE ::= "\"" ;
 VCHAR ::= "!".."~" ;
 quoted_pair ::= "\\" ( VCHAR11 | WSP7 ) | obs_qp96 ;
 FWS ::= [ { WSP7 } CRLF5 ] WSP7 { WSP7 } | obs_FWS101 ;
 ctext ::= "!".."'" | "*".."[" | "]".."~" | obs_ctext93 ;
 ccontent ::= ctext14 | quoted_pair12 | comment16 ;
 comment ::= "(" { [ FWS13 ] ccontent15 } [ FWS13 ] ")" ;
 CFWS ::= [ FWS13 ] comment16 { [ FWS13 ] comment16 } [ FWS13 ] | FWS13 ;
 atext ::= ALPHA8 | DIGIT9 | "!" | "#" | "$" | "%" | "&" | "'" | "*" | "+" | "-" | "/" | "=" | "?" | "^" | "_" | "`" | "{" | "|" | "}" | "~" ;
 atom ::= [ CFWS17 ] atext18 { atext18 } [ CFWS17 ] ;
 dot_atom_text ::= atext18 { atext18 } { "." atext18 { atext18 } } ;
 dot_atom ::= [ CFWS17 ] dot_atom_text20 [ CFWS17 ] ;
 specials ::= "(" | ")" | "<" | ">" | "[" | "]" | ":" | ";" | "@" | "\\" | "," | "." | DQUOTE10 ;
 qtext ::= "!" | "#".."[" | "]".."~" | obs_qtext94 ;
 qcontent ::= qtext23 | quoted_pair12 ;
 quoted_string ::= [ CFWS17 ] DQUOTE10 { [ FWS13 ] qcontent24 } [ FWS13 ] DQUOTE10 [ CFWS17 ] ;
 word ::= atom19 | quoted_string25 ;
 phrase ::= word26 { word26 } | obs_phrase99 ;
 unstructured ::= { [ FWS13 ] VCHAR11 } { WSP7 } | obs_unstruct98 ;
 date_time ::= [ day_of_week30 "," ] date32 time36 [ CFWS17 ] ;
 day_of_week ::= [ FWS13 ] day_name31 | obs_day_of_week102 ;
 day_name ::= "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun" ;
 date ::= day33 month34 year35 ;
 day ::= [ FWS13 ] DIGIT9 [ DIGIT9 ] FWS13 | obs_day103 ;
 month ::= "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec" ;
 year ::= FWS13 DIGIT9 DIGIT9 DIGIT9 DIGIT9 FWS13 | obs_year104 ;
 time ::= time_of_day37 zone41 ;
 time_of_day ::= hour38 ":" minute39 [ ":" second40 ] ;
 hour ::= DIGIT9 DIGIT9 | obs_hour105 ;
 minute ::= DIGIT9 DIGIT9 | obs_minute106 ;
 second ::= DIGIT9 DIGIT9 | obs_second107 ;
 zone ::= FWS13 ( "+" | "-" ) DIGIT9 DIGIT9 DIGIT9 DIGIT9 | obs_zone108 ;
 address ::= mailbox43 | group46 ;
 mailbox ::= name_addr44 | addr_spec51 ;
 name_addr ::= [ display_name47 ] angle_addr45 ;
 angle_addr ::= [ CFWS17 ] "<" addr_spec51 ">" [ CFWS17 ] | obs_angle_addr109 ;
 group ::= display_name47 ":" [ group_list50 ] ";" [ CFWS17 ] ;
 display_name ::= phrase27 ;
 mailbox_list ::= mailbox43 { "," mailbox43 } | obs_mbox_list112 ;
 address_list ::= address42 { "," address42 } | obs_addr_list113 ;
 group_list ::= mailbox_list48 | CFWS17 | obs_group_list114 ;
 addr_spec ::= local_part52 "@" domain53 ;
 local_part ::= dot_atom21 | quoted_string25 | obs_local_part115 ;
 domain ::= dot_atom21 | domain_literal54 | obs_domain116 ;
 domain_literal ::= [ CFWS17 ] "[" { [ FWS13 ] dtext55 } [ FWS13 ] "]" [ CFWS17 ] ;
 dtext ::= "!".."Z" | "^".."~" | obs_dtext117 ;
 message ::= ( fields59 | obs_fields118 ) [ CRLF5 body57 ] ;
 body ::= { { text58 } CRLF5 } { text58 } | obs_body97 ;
 text ::= "ASCII codes: 1-9, 11, 12, 14-127" ;
 fields ::= { trace84 { optional_field89 } | { resent_date77 | resent_from78 | resent_sender79 | resent_to80 | resent_cc81 | resent_bcc82 | resent_msg_id83 } } { orig_date60 | from61 | sender62 | reply_to63 | to64 | cc65 | bcc66 | message_id67 | in_reply_to68 | references69 | subject74 | comments75 | keywords76 | optional_field89 } ;
 orig_date ::= "Date:" date_time29 CRLF5 ;
 from ::= "From:" mailbox_list48 CRLF5 ;
 sender ::= "Sender:" mailbox43 CRLF5 ;
 reply_to ::= "Reply_To:" address_list49 CRLF5 ;
 to ::= "To:" address_list49 CRLF5 ;
 cc ::= "Cc:" address_list49 CRLF5 ;
 bcc ::= "Bcc:" [ address_list49 | CFWS17 ] CRLF5 ;
 message_id ::= "Message_ID:" msg_id70 CRLF5 ;
 in_reply_to ::= "In_Reply_To:" msg_id70 { msg_id70 } CRLF5 ;
 references ::= "References:" msg_id70 { msg_id70 } CRLF5 ;
 msg_id ::= [ CFWS17 ] "<" id_left71 "@" id_right72 ">" [ CFWS17 ] ;
 id_left ::= dot_atom_text20 | obs_id_left129 ;
 id_right ::= dot_atom_text20 | no_fold_literal73 | obs_id_right130 ;
 no_fold_literal ::= "[" { dtext55 } "]" ;
 subject ::= "Subject:" unstructured28 CRLF5 ;
 comments ::= "Comments:" unstructured28 CRLF5 ;
 keywords ::= "Keywords:" phrase27 { "," phrase27 } CRLF5 ;
 resent_date ::= "Resent_Date:" date_time29 CRLF5 ;
 resent_from ::= "Resent_From:" mailbox_list48 CRLF5 ;
 resent_sender ::= "Resent_Sender:" mailbox43 CRLF5 ;
 resent_to ::= "Resent_To:" address_list49 CRLF5 ;
 resent_cc ::= "Resent_Cc:" address_list49 CRLF5 ;
 resent_bcc ::= "Resent_Bcc:" [ address_list49 | CFWS17 ] CRLF5 ;
 resent_msg_id ::= "Resent_Message_ID:" msg_id70 CRLF5 ;
 trace ::= [ return85 ] received87 { received87 } ;
 return ::= "Return_Path:" path86 CRLF5 ;
 path ::= angle_addr45 | [ CFWS17 ] "<" [ CFWS17 ] ">" [ CFWS17 ] ;
 received ::= "Received:" { received_token88 } ";" date_time29 CRLF5 ;
 received_token ::= word26 | angle_addr45 | addr_spec51 | domain53 ;
 optional_field ::= field_name90 ":" unstructured28 CRLF5 ;
 field_name ::= ftext91 { ftext91 } ;
 ftext ::= "!".."9" | ";".."~" ;
 obs_NO_WS_CTL ::= "ASCII codes 1-8, 11, 12, 14-31, 127" ;
 obs_ctext ::= obs_NO_WS_CTL92 ;
 obs_qtext ::= obs_NO_WS_CTL92 ;
 obs_utext ::= NUL1 | obs_NO_WS_CTL92 | VCHAR11 ;
 obs_qp ::= "\\" ( NUL1 | obs_NO_WS_CTL92 | LF3 | CR4 ) ;
 obs_body ::= { { LF3 } { CR4 } { ( NUL1 | text58 ) { LF3 } { CR4 } } | CRLF5 } ;
 obs_unstruct ::= { { LF3 } { CR4 } { obs_utext95 { LF3 } { CR4 } } | FWS13 } ;
 obs_phrase ::= word26 { word26 | "." | CFWS17 } ;
 obs_phrase_list ::= [ phrase27 | CFWS17 ] { "," [ phrase27 | CFWS17 ] } ;
 obs_FWS ::= WSP7 { WSP7 } { CRLF5 WSP7 { WSP7 } } ;
 obs_day_of_week ::= [ CFWS17 ] day_name31 [ CFWS17 ] ;
 obs_day ::= [ CFWS17 ] DIGIT9 [ DIGIT9 ] [ CFWS17 ] ;
 obs_year ::= [ CFWS17 ] DIGIT9 DIGIT9 { DIGIT9 } [ CFWS17 ] ;
 obs_hour ::= [ CFWS17 ] DIGIT9 DIGIT9 [ CFWS17 ] ;
 obs_minute ::= [ CFWS17 ] DIGIT9 DIGIT9 [ CFWS17 ] ;
 obs_second ::= [ CFWS17 ] DIGIT9 DIGIT9 [ CFWS17 ] ;
 obs_zone ::= "UT" | "GMT" | "EST" | "EDT" | "CST" | "CDT" | "MST" | "MDT" | "PST" | "PDT" | "A".."I" | "K".."Z" | "a".."i" | "k".."z" ;
 obs_angle_addr ::= [ CFWS17 ] "<" obs_route110 addr_spec51 ">" [ CFWS17 ] ;
 obs_route ::= obs_domain_list111 ":" ;
 obs_domain_list ::= { CFWS17 | "," } "@" domain53 { "," [ CFWS17 ] [ "@" domain53 ] } ;
 obs_mbox_list ::= { [ CFWS17 ] "," } mailbox43 { "," [ mailbox43 | CFWS17 ] } ;
 obs_addr_list ::= { [ CFWS17 ] "," } address42 { "," [ address42 | CFWS17 ] } ;
 obs_group_list ::= [ CFWS17 ] "," { [ CFWS17 ] "," } [ CFWS17 ] ;
 obs_local_part ::= word26 { "." word26 } ;
 obs_domain ::= atom19 { "." atom19 } ;
 obs_dtext ::= obs_NO_WS_CTL92 | quoted_pair12 ;
 obs_fields ::= { obs_return142 | obs_received143 | obs_orig_date119 | obs_from120 | obs_sender121 | obs_reply_to122 | obs_to123 | obs_cc124 | obs_bcc125 | obs_message_id126 | obs_in_reply_to127 | obs_references128 | obs_subject131 | obs_comments132 | obs_keywords133 | obs_resent_date136 | obs_resent_from134 | obs_resent_send135 | obs_resent_rply141 | obs_resent_to137 | obs_resent_cc138 | obs_resent_bcc139 | obs_resent_mid140 | obs_optional144 } ;
 obs_orig_date ::= "Date" { WSP7 } ":" date_time29 CRLF5 ;
 obs_from ::= "From" { WSP7 } ":" mailbox_list48 CRLF5 ;
 obs_sender ::= "Sender" { WSP7 } ":" mailbox43 CRLF5 ;
 obs_reply_to ::= "Reply_To" { WSP7 } ":" address_list49 CRLF5 ;
 obs_to ::= "To" { WSP7 } ":" address_list49 CRLF5 ;
 obs_cc ::= "Cc" { WSP7 } ":" address_list49 CRLF5 ;
 obs_bcc ::= "Bcc" { WSP7 } ":" ( address_list49 | { [ CFWS17 ] "," } [ CFWS17 ] ) CRLF5 ;
 obs_message_id ::= "Message_ID" { WSP7 } ":" msg_id70 CRLF5 ;
 obs_in_reply_to ::= "In_Reply_To" { WSP7 } ":" { phrase27 | msg_id70 } CRLF5 ;
 obs_references ::= "References" { WSP7 } ":" { phrase27 | msg_id70 } CRLF5 ;
 obs_id_left ::= local_part52 ;
 obs_id_right ::= domain53 ;
 obs_subject ::= "Subject" { WSP7 } ":" unstructured28 CRLF5 ;
 obs_comments ::= "Comments" { WSP7 } ":" unstructured28 CRLF5 ;
 obs_keywords ::= "Keywords" { WSP7 } ":" obs_phrase_list100 CRLF5 ;
 obs_resent_from ::= "Resent_From" { WSP7 } ":" mailbox_list48 CRLF5 ;
 obs_resent_send ::= "Resent_Sender" { WSP7 } ":" mailbox43 CRLF5 ;
 obs_resent_date ::= "Resent_Date" { WSP7 } ":" date_time29 CRLF5 ;
 obs_resent_to ::= "Resent_To" { WSP7 } ":" address_list49 CRLF5 ;
 obs_resent_cc ::= "Resent_Cc" { WSP7 } ":" address_list49 CRLF5 ;
 obs_resent_bcc ::= "Resent_Bcc" { WSP7 } ":" ( address_list49 | { [ CFWS17 ] "," } [ CFWS17 ] ) CRLF5 ;
 obs_resent_mid ::= "Resent_Message_ID" { WSP7 } ":" msg_id70 CRLF5 ;
 obs_resent_rply ::= "Resent_Reply_To" { WSP7 } ":" address_list49 CRLF5 ;
 obs_return ::= "Return_Path" { WSP7 } ":" path86 CRLF5 ;
 obs_received ::= "Received" { WSP7 } ":" { received_token88 } CRLF5 ;
 obs_optional ::= field_name90 { WSP7 } ":" unstructured28 CRLF5 ;

Relations JPA

27 April 2019

Cas interessant pour Email, qui doit faire le lien vers de multiples destinataires et de multiples cc.

Ce que je voudrais faire :

avoir une fonction getTo, qui renvoie une liste de string avec les adresses, et une fonction getCc, qui fait la même chose.

Je vais essayer de m'approcher de ça.

Mapping avec des Entités EmailParticipant.

Une bonne analyse sur la question de la composition (vs agrégation) :
* JPA Aggregation versus composition and deleting of child entities

Comme il n'y à pas d'Id spécifique, il faut créer une sous classe composite sur le modèle de :

J'ai une classe EmailParticipant, qui possède un EmailParticipantId avec les même champs.

On peut simplifier avec ClassId en indiquant la classe elle même.
* SQL JPA - Multiple columns as primary key
** La bonne réponse

Bilan provisoire

On arrive au resultat suivant :

{
	"emailAccount":"devkdm@festival-cannes.fr",
	"emailUid":"test1",
	"subject":"Ce mail n'existe pas",
	"from":"hnlocher@cst.fr",
	"sendDate":"2019-04-24T07:08:00.000+0000",
	"emldirpath":"2019.04.10+10.52.23-2-%3Cdfdce60f-1d3d-403f-2c2b-c5b642d296a6%40cst.fr%3E/",
	"emlfilename":"mail.eml",
	"participants":
		[
			{"relation":"TO","address":"devkdm@festival-cannes.fr"},
			{"relation":"CC","address":"hnlocher@free.fr"}
		]
	}

J'aurais voulu plutot :

"to":
	[
		{"devkdm@festival-cannes.fr"}

	]
"cc":
	[
		{"hnlocher@free.fr"}
	]

Mais ça va être trop long à obtenir pour cette année.

Il y a une piste ici :

Sauvegarde des Emails

L'enregistrement des emails sans participants (TO et CC) et sans attachements d'assets fonctionne rapidement.

Par contre, lorsque l'on ajoute des collections en liaison oneToMany, l'enregistrement ne se fait pas avec la configuration actuelle, ou l'on reprend le champ clef étrangère.

La clef étrangère n'est pas initialisée au moment de l'écriture, et l'écriture échoue. Au moins, la cascade fonctionne.

En rendant la liaison bidirectionnelle (ce qui est idiot pour les participants, qui sont clairement en composition), la sauvegarde fonctionne.

Mise en oeuvre dans kdmMailFetcher

Tout fonctionne au moment des tests, mais lors de l'enregistrement du mail depuis kdmMailFetcher il se passe la chose suivante :

2019-04-28 09:32:10.916  INFO 3772 --- [           main] logFonctionnel                           :                   Email from : hnlocher@cst.fr
>>>                       Id : 12
>>>                  Subject : Fwd: Insider with enclosings
>>>                     Date : 2019.04.15 21.53.02
>>>                       To : devkdm@festival-cannes.fr
>>>                       Cc :
>>>                      Bcc :
>>> Nombre de pièces jointes : 1
>>>                    X-UID : null
>>>               Message-ID : <0f7971ed97aef73c5c8ec91fa9d1fb9b@cst.fr>
>>>          Is an autoreply :no
2019-04-28 09:32:10.947 DEBUG 3772 --- [           main] o.h.SQL                                  :
    insert
    into
        email
        (emailaccount, uid, emldirpath, emlfilename, sender, senddate, subject)
    values
        (?, ?, ?, ?, ?, ?, ?)
Hibernate:
    insert
    into
        email
        (emailaccount, uid, emldirpath, emlfilename, sender, senddate, subject)
    values
        (?, ?, ?, ?, ?, ?, ?)
2019-04-28 09:32:10.947 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [1] as [VARCHAR] - [TODO]
2019-04-28 09:32:10.947 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [2] as [VARCHAR] - [<0f7971ed97aef73c5c8ec91fa9d1fb9b@cst.fr>]
2019-04-28 09:32:10.947 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [3] as [VARCHAR] - [/to/do/]
2019-04-28 09:32:10.947 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [4] as [VARCHAR] - [todo.txt]
2019-04-28 09:32:10.947 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [5] as [VARCHAR] - [hnlocher@cst.fr]
2019-04-28 09:32:10.947 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [6] as [TIMESTAMP] - [Mon Apr 15 21:53:02 CEST 2019]
2019-04-28 09:32:10.947 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [7] as [VARCHAR] - [Fwd: Insider with enclosings]
2019-04-28 09:32:10.963 DEBUG 3772 --- [           main] o.h.SQL                                  :
    insert
    into
        emailassetpresence
        (email_idemail, assetuuid, assetkind, creationtime, filename, path)
    values
        (?, ?, ?, ?, ?, ?)
Hibernate:
    insert
    into
        emailassetpresence
        (email_idemail, assetuuid, assetkind, creationtime, filename, path)
    values
        (?, ?, ?, ?, ?, ?)
2019-04-28 09:32:10.963 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [1] as [INTEGER] - [25]
2019-04-28 09:32:10.963 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [2] as [VARCHAR] - [urn:uuid:2e564f38-3f0f-4f7a-a0fb-3786af07d8a3]
2019-04-28 09:32:10.963 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [3] as [VARCHAR] - [KDM]
2019-04-28 09:32:10.963 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [4] as [TIMESTAMP] - [null]
2019-04-28 09:32:10.963 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [5] as [VARCHAR] - [null]
2019-04-28 09:32:10.963 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [6] as [VARCHAR] - [null]
2019-04-28 09:32:10.963 DEBUG 3772 --- [           main] o.h.SQL                                  :
    insert
    into
        emailassetpresence
        (email_idemail, assetuuid, assetkind, creationtime, filename, path)
    values
        (?, ?, ?, ?, ?, ?)
Hibernate:
    insert
    into
        emailassetpresence
        (email_idemail, assetuuid, assetkind, creationtime, filename, path)
    values
        (?, ?, ?, ?, ?, ?)
2019-04-28 09:32:10.963 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [1] as [INTEGER] - [25]
2019-04-28 09:32:10.963 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [2] as [VARCHAR] - [urn:uuid:408139f6-70ab-4ed8-9fd1-c63ce1dc0d30]
2019-04-28 09:32:10.963 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [3] as [VARCHAR] - [CPL]
2019-04-28 09:32:10.963 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [4] as [TIMESTAMP] - [null]
2019-04-28 09:32:10.963 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [5] as [VARCHAR] - [null]
2019-04-28 09:32:10.963 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [6] as [VARCHAR] - [null]
2019-04-28 09:32:10.963 DEBUG 3772 --- [           main] o.h.SQL                                  :
    insert
    into
        emailparticipant
        (relation, email_idemail, adress)
    values
        (?, ?, ?)
Hibernate:
    insert
    into
        emailparticipant
        (relation, email_idemail, adress)
    values
        (?, ?, ?)
2019-04-28 09:32:10.979 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [1] as [VARCHAR] - [TO]
2019-04-28 09:32:10.979 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [2] as [INTEGER] - [25]
2019-04-28 09:32:10.979 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [3] as [VARCHAR] - [devkdm@festival-cannes.fr]
2019-04-28 09:32:10.979 DEBUG 3772 --- [           main] o.h.SQL                                  :
    insert
    into
        emailparticipant
        (relation, email_idemail, adress)
    values
        (?, ?, ?)
Hibernate:
    insert
    into
        emailparticipant
        (relation, email_idemail, adress)
    values
        (?, ?, ?)
2019-04-28 09:32:10.979 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [1] as [VARCHAR] - [CC]
2019-04-28 09:32:10.979 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [2] as [INTEGER] - [25]
2019-04-28 09:32:10.979 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [3] as [VARCHAR] - [devkdm@festival-cannes.fr]
2019-04-28 09:32:10.994 DEBUG 3772 --- [           main] o.h.SQL                                  :
    update
        emailassetpresence
    set
        email_idemail=?
    where
        email_idemail=?
        and assetuuid=?
        and assetkind=?
        and creationtime=?
        and filename=?
        and path=?
Hibernate:
    update
        emailassetpresence
    set
        email_idemail=?
    where
        email_idemail=?
        and assetuuid=?
        and assetkind=?
        and creationtime=?
        and filename=?
        and path=?
2019-04-28 09:32:10.994 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [1] as [INTEGER] - [25]
2019-04-28 09:32:10.994 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [2] as [INTEGER] - [25]
2019-04-28 09:32:10.994 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [3] as [VARCHAR] - [urn:uuid:2e564f38-3f0f-4f7a-a0fb-3786af07d8a3]
2019-04-28 09:32:10.994 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [4] as [VARCHAR] - [KDM]
2019-04-28 09:32:10.994 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [5] as [TIMESTAMP] - [null]
2019-04-28 09:32:10.994 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [6] as [VARCHAR] - [null]
2019-04-28 09:32:10.994 TRACE 3772 --- [           main] o.h.t.d.s.BasicBinder                    : binding parameter [7] as [VARCHAR] - [null]
2019-04-28 09:32:10.994 ERROR 3772 --- [           main] o.h.i.ExceptionMapperStandardImpl        : HHH000346: Error during managed flush [Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1]
2019-04-28 09:32:10.994  INFO 3772 --- [           main] o.h.e.j.b.i.AbstractBatchImpl            : HHH000010: On release of batch it still contained JDBC statements
2019-04-28 09:32:11.010 ERROR 3772 --- [           main] logTechnique                             : Erreur lors de l'appel au handler

org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
	at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:338) ~[spring-orm-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:253) ~[spring-orm-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:536) ~[spring-orm-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:746) ~[spring-tx-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:714) ~[spring-tx-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:533) ~[spring-tx-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:304) ~[spring-tx-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) ~[spring-tx-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ~[spring-tx-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135) ~[spring-data-jpa-2.1.4.RELEASE.jar:2.1.4.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at com.sun.proxy.$Proxy123.save(Unknown Source) ~[?:?]
	at fr.cst.rd.digitalCinemaAssetHandler.services.EmailService.saveEmail(EmailService.java:31) ~[digitalCinemaAssetHandler-0.0.20-SNAPSHOT.jar:0.0.20-SNAPSHOT]
	at fr.cst.rd.kdmmailfetcher.db.SerialisationAssets.terminate(SerialisationAssets.java:226) ~[classes/:?]
	at fr.cst.rd.kdmmailfetcher.DigitalCinemaFileHandler.terminate(DigitalCinemaFileHandler.java:638) ~[classes/:?]
	at fr.cst.rd.kdmmailfetcher.DigitalCinemaEmailHandler.handleEmail(DigitalCinemaEmailHandler.java:66) ~[classes/:?]
	at fr.cst.rd.kdmmailfetcher.MailBox$UnreadMails.processFetchedMails(MailBox.java:66) [classes/:?]
	at fr.cst.rd.kdmmailfetcher.KdmMailFetcherJob.doJob(KdmMailFetcherJob.java:100) [classes/:?]
	at fr.cst.rd.kdmmailfetcher.KdmMailFetcher.run(KdmMailFetcher.java:59) [classes/:?]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:813) [spring-boot-2.1.2.RELEASE.jar:2.1.2.RELEASE]
	at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:797) [spring-boot-2.1.2.RELEASE.jar:2.1.2.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:324) [spring-boot-2.1.2.RELEASE.jar:2.1.2.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) [spring-boot-2.1.2.RELEASE.jar:2.1.2.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) [spring-boot-2.1.2.RELEASE.jar:2.1.2.RELEASE]
	at fr.cst.rd.kdmmailfetcher.KdmMailFetcher.main(KdmMailFetcher.java:37) [classes/:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_121]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_121]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_121]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_121]
	at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:558) [spring-boot-maven-plugin-2.1.2.RELEASE.jar:2.1.2.RELEASE]
	at java.lang.Thread.run(Thread.java:745) [?:1.8.0_121]
Caused by: org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
	at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:67) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:54) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:46) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:1340) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.persister.collection.OneToManyPersister.recreate(OneToManyPersister.java:186) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.action.internal.CollectionRecreateAction.execute(CollectionRecreateAction.java:50) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:478) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:356) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1454) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:511) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3283) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2479) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:178) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:39) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:271) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:98) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:532) ~[spring-orm-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	... 35 more

L'assset urn:uuid:2e564f38-3f0f-4f7a-a0fb-3786af07d8a3 à déjà été écrite, il y a un update de fait dessus. Pourquoi ?

Réponse, c'était l'autre bout le la liaison qui écrit à son tour.

Il faut mettre @JoinColumn(... insertable="false", updatetable="false") du coté opposé.

Other références

Mappage d'entités

23 April 2019

Mapping des timestamp

On peut éviter l'accesseur setInsertTime.

Exemple :

@Basic(optional = false)
@Column(name = "LastTouched", insertable = false, updatable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date lastTouched;

La même chose en hiberate :

	@Column
	@CreationTimestamp
	private LocalDateTime createDateTime;

	@Column
	@UpdateTimestamp
	private LocalDateTime updateDateTime;

Utilisation des NaturalId

17 April 2019

Une idée pour utiliser les uuid comme naturalId, et garder des identifiants courts pour des raisons de perf.

https://vladmihalcea.com/the-best-way-to-map-a-naturalid-business-key-with-jpa-and-hibernate/

Exemple d'utilisation :
https://vladmihalcea.com/the-best-way-to-map-a-many-to-many-association-with-extra-columns-when-using-jpa-and-hibernate/

Note : exemple d'école, l'utilisation d'un many-to-many est beaucoup plus efficace (https://vladmihalcea.com/the-best-way-to-use-the-manytomany-annotation-with-jpa-and-hibernate/)

Insertion de PlantUML dans Markdown

16 April 2019

https://blog.anoff.io/2018-07-31-diagrams-with-plantuml/

Liens symboliques sous Windows

12 April 2019

Catalogue d'icones dans Bootstrap 4

07 April 2019

Dans Bootstrap 4, les fontes on disparu. On peut utiliser FontAwesome à la plage (https://fontawesome.com/icons?d=gallery).

Autre

Présentations avec Mdx-Deck

07 April 2019

Lien schémas avec Graphviz

06 April 2019

Graphviz links

https://github.com/magjac/d3-graphviz
https://bl.ocks.org/magjac/4acffdb3afbc4f71b448a210b5060bca

Invalid CORS request

06 April 2019

Lorsque l'on utilise les templates de façon statique, et que l'on remplace les url par celles de l'application en train d'excuter l'API, le tableau n'est pas correctement chargé :

L'appel à l'API restful échoue avec un Status Code 403 :

Invalid CORS request

Dans mon cas, postman est très heureux lorsque l'on appelle les URL.

Il s'agit de Spring Boot qui filtre, peut-être ?

En tout cas firefox et chrome réagissent de la même façon.

Un post sur le sujet, pour désactiver le filtrage CORS dans spring boot.
https://medium.com/@valeryyakovlev/how-to-disable-cors-restrictions-in-spring-boot-app-819158aa3919

Spring Boot générer un war fonctionnel

06 April 2019

Pour produire un fichier .war utilisable dans un serveur tomcat à partir d'une application Spring Boot, tout d'abord, préciser dans le .pom que l'on veut un .war comme package.

Comme indiqué dans de nombreux exemples sur le web, le .war ainsi produit n'est pas fonctionnel, puisqu'il n'y a pas de fichier web.xml de produit, et donc aucune servlet à démarrer.

D'après le bouquin "Spring Boot in action" de chez Manning, au chapritre 8, Il faut tout d'abord ajouter une configuration d'initialiazation.

En adaptant l'exemple fourni, cela donne le point suivant :

package fr.cst.rd.digitalCinemaAssetHandler;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

public class DigitalCinemaAssetHandlerServletInitializer extends SpringBootServletInitializer {

	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
		return builder.sources(DigitalCinemaAssetHandlerApplication.class);
	}

}

Tomcat embarqué mvn tomcat7:run

Note les dates de logs ne reflete pas l'odre des essais, puisque j'ai recommençé pour rédiger cette note.

[...]
avr. 06, 2019 3:57:28 PM org.apache.catalina.startup.ContextConfig processAnnotationsJar
GRAVE: Unable to process Jar entry [module-info.class] from Jar [jar:file:/C:/Users/hnl-bis/workspace/dicranum/target/digitalCinemaAssetHandler-0.0.3-SNAPSHOT/WEB-INF/lib/jaxb-api-2.3.1.jar!/] for annotations
org.apache.tomcat.util.bcel.classfile.ClassFormatException: Invalid byte tag in constant pool: 19
	at org.apache.tomcat.util.bcel.classfile.Constant.readConstant(Constant.java:133)
[...]

Tomcat 7 de XAMPP

En essayant de le charger dans le tomcat 7 de XAMPP erreur concernant la compatibilité du timecode, malgré le fait que la jvm utilisée soit java 8.

GRAVE: Unable to process Jar entry [META-INF/versions/9/module-info.class] from Jar [jar:file:/C:/dev/xampp/tomcat/webapps/digitalCinemaAssetHandler-0.0.2-SNAPSHOT/WEB-INF/lib/byte-buddy-1.9.7.jar!/] for annotations
org.apache.tomcat.util.bcel.classfile.ClassFormatException: Invalid byte tag in constant pool: 19
	at org.apache.tomcat.util.bcel.classfile.Constant.readConstant(Constant.java:97)
	at org.apache.tomcat.util.bcel.classfile.ConstantPool.<init>(ConstantPool.java:55)
	at org.apache.tomcat.util.bcel.classfile.ClassParser.readConstantPool(ClassParser.java:175)
	at org.apache.tomcat.util.bcel.classfile.ClassParser.parse(ClassParser.java:83)
	at org.apache.catalina.startup.ContextConfig.processAnnotationsStream(ContextConfig.java:2071)
	at org.apache.catalina.startup.ContextConfig.processAnnotationsJar(ContextConfig.java:1947)
	at org.apache.catalina.startup.ContextConfig.processAnnotationsUrl(ContextConfig.java:1913)
	at org.apache.catalina.startup.ContextConfig.processAnnotations(ContextConfig.java:1898)
	at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1330)
	at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:889)
	at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:386)
	at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
	at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5380)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
	at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
	at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
	at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:649)
	at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1083)
	at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1879)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

Il y a un problème binaire avec les bytecode.

Avec tomcat 9

Téléchargement d'un tomcat 9.

En plaçant la webapp :

06-Apr-2019 15:38:57.530 GRAVE [http-nio-8080-exec-1] org.apache.catalina.core.ApplicationContext.log HTMLManager: Erreur lors du démarrage de [/digitalCinemaAssetHandler-0.0.2-SNAPSHOT]
 org.apache.catalina.LifecycleException: Echec de démarrage du composant [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/digitalCinemaAssetHandler-0.0.2-SNAPSHOT]]
	at org.apache.catalina.util.LifecycleBase.handleSubClassException(LifecycleBase.java:441)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:198)
	at org.apache.catalina.manager.ManagerServlet.start(ManagerServlet.java:1409)
	at org.apache.catalina.manager.HTMLManagerServlet.start(HTMLManagerServlet.java:700)
	at org.apache.catalina.manager.HTMLManagerServlet.doPost(HTMLManagerServlet.java:223)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.filters.CsrfPreventionFilter.doFilter(CsrfPreventionFilter.java:136)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:607)
	at org.apache.catalina.valves.RequestFilterValve.process(RequestFilterValve.java:348)
	at org.apache.catalina.valves.RemoteAddrValve.invoke(RemoteAddrValve.java:53)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:678)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1745)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:576)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1083)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:853)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
	at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:157)
	at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:137)
	at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:91)
	at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:171)
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5139)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
	... 32 more
Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:275)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:237)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
	at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.injectServices(DefaultIdentifierGeneratorFactory.java:152)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.injectDependencies(AbstractServiceRegistryImpl.java:286)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:243)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
	at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.<init>(InFlightMetadataCollectorImpl.java:179)
	at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:119)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:904)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:935)
	at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57)
	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390)
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377)
	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1804)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1741)
	... 51 more
Caused by: org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
	at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.determineDialect(DialectFactoryImpl.java:100)
	at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.buildDialect(DialectFactoryImpl.java:54)
	at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:137)
	at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35)
	at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:94)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263)
	... 68 more

En fait, il faut pouvoir charger bean Spring, les annotations, etc. Il faut donc que le point d'entrée le permette...

Gestions des chemins

Lorsque l'application est servie depuis un .war, les chemins ne fonctionnent plus, il faut les corriger avec le mécanisme de templates.

=> Cela fonctionne correctemetn pour les éléments dynamiques.

Par exemple :

<a class="nav-link" href="#" th:href="@{/ajax/cpl}" >CPL<span class="sr-only" th:if="${module == 'cpl'}">(current)</span></a>

est correctement substitué en :

 <a class="nav-link" href="/digitalCinemaAssetHandler-0.0.17-SNAPSHOT/ajax/cpl" >CPL<span class="sr-only">(current)</span></a>

Par contre les éléments statiques ne sont pas correctement substitués.

<img src="images/dicranum-inverse.svg" alt="logo"  height="32px" width="32px" th:src="@{~/images/dicranum-inverse.svg}">

Est substitué en :

<img src="/images/dicranum-inverse.svg" alt="logo"  height="32px" width="32px">

Pourtant, le resources est correctement servie en :
http://localhost:8080/digitalCinemaAssetHandler-0.0.17-SNAPSHOT/images/dicranum-inverse.svg

Un problème de template ?

Dans un certains nombre de problèmes rencontrés sur les forums, le problème est que le contenu du répertoire /src/main/resources/static n'est pas servi, ou que les pages publiques n'ont pas été rangées à cet endroit. Ce n'est pas mon problème.

En fait, il s'agissait plus d'un problème de de template :
Il faut écrire :

th:src="@{/images/dicranum-inverse.svg}"

et non

th:src="@{~/images/dicranum-inverse.svg}"

Changement de la configuration, et retour en arrière

J'ai à un moment remplaçé la configuration par extension d'interface :

package fr.cst.rd.digitalCinemaAssetHandler.webgui;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MvcConfiguration implements WebMvcConfigurer {

    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home");
    }

}

Par une configuration par extension d'adapter, pour palier à la situation précédente avant de comprendre qu'il s'agissait des templates.

package fr.cst.rd.digitalCinemaAssetHandler.webgui;

import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MvcConfigurer extends WebMvcAutoConfiguration
{

}

Mais cela n'apportait rien. Je le garde dans les notes just in case.

war.original and repackage

Comme il y a une instruction repackage dans le .pom, le .war est renomé .war.original, et la version repackage
* SO : why spring boot generate jar or war file with .original extention?

Cette action doit apparaitre dans le parent .pom.

Erreurs de chargement lorsqu'une autre version est encore chargée

21-Apr-2019 08:56:59.845 INFOS [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.webresources.Cache.backgroundProcess The background cache eviction process was unable to free [10] percent of the cache for Context [/digitalCinemaAssetHandler-0.0.17-bis-SNAPSHOT] - consider increasing the maximum size of the cache. After eviction approximately [9,454] KB of data remained in the cache.

A explorer ...

Pour servir sur un autre port

Il faut créer un nouveau service :

Ajouter dans server.xml (tomcat8.0.53) :

    <Service name="QuousqueService">
    <Connector port="8091" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8444" />
    <!--<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />-->
    <Engine name="Catalina2" defaultHost="localhost2">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <Host name="localhost2"  appBase="webapps2"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost2_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

      </Host>
    </Engine>
  </Service>

Web décentralisé : le projet Solid

06 April 2019

Liens à creuser

Push depuis eclipse vers github

24 March 2019

J'essaye d'éviter d'installer git pour windows avec son bash minimal, qui ne fait pas une installation très propre.

Création Clef ssh sur github

Tout d'abord, création de la clef sur github en copiant la clef publique interprétée, par exemple en la chargeant dasn puttyGen.

Ensuite, en utilisant pageant, chargement de la clef privée et saisie du mot de passe.

En utilisant putty pour se connecter avec
git@github.com

Option "Dont start a shell or other command at all" dans l'onglet ssh.

En regardant les logs dans eventLog :
2019-03-24 18:40:16 Connecting to 140.82.118.3 port 22
2019-03-24 18:40:16 We claim version: SSH-2.0-PuTTY_Release_0.68
2019-03-24 18:40:17 Server version: SSH-2.0-babeld-c6e8f0d0
2019-03-24 18:40:17 Using SSH protocol version 2
2019-03-24 18:40:17 Doing ECDH key exchange with curve Curve25519 and hash SHA-256
2019-03-24 18:40:17 Server also has ssh-dss host key, but we don't know it
2019-03-24 18:40:17 Host key fingerprint is:
2019-03-24 18:40:17 ssh-rsa 2048 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48
2019-03-24 18:40:17 Initialised AES-256 SDCTR client->server encryption
2019-03-24 18:40:17 Initialised HMAC-SHA-256 client->server MAC algorithm
2019-03-24 18:40:17 Initialised AES-256 SDCTR server->client encryption
2019-03-24 18:40:17 Initialised HMAC-SHA-256 server->client MAC algorithm
2019-03-24 18:40:17 Pageant is running. Requesting keys.
2019-03-24 18:40:17 Pageant has 1 SSH-2 keys
2019-03-24 18:40:17 Trying Pageant key #0
2019-03-24 18:40:17 Sending Pageant's response
2019-03-24 18:40:17 Access granted

La connection à l'air de fonctionner.

Installation d'un proxy dans eclipse pour fonctionner avec pageant

https://eclipseguru.github.io/eclipse-jsch-agent-proxy/

Les éléments installés :
Warning: You are installing software that contains unsigned content. The authenticity or validity of this software cannot be established. Do you want to continue with the installation?
C:\Users\hnl-bis.p2\pool\plugins\com.jcraft.jsch.agentproxy.eclipse.sshagent_1.0.0.201606231331.jar
C:\Users\hnl-bis.p2\pool\features\com.jcraft.jsch.agentproxy.eclipse.sshagent_1.0.0.201606231331

Dans eclipse, configuration ssh2. J'utilise ~.ssh par défaut (au lieu de ~/.ssh).

Ajout de la clef privée (et publique) dans le répertoire .ssh.

Configuration de git pour utiliser JSch.

Lors du push, la passphrase est demandée, et on peut continuer.

Problème de fichier trop grand

Repository git@github.com:CSTIS/canhelp.git

pre-receive hook declined
error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.
error: Trace: 0bbe2b4aaddf39dddf7bdc4b3a53515a
error: See http://git.io/iEPt8g for more information.
error: File bdd_archives/canhelp2016_cannes2015_dev-2016-01-18-12h15.sql is 248.94 MB; this exceeds GitHub's file size limit of 100.00 MB

Il faut donc retirer ce fichier de l'historique. (Pour des raisons de confidentialité).

Nettoyage du dépot

commit 054c76c49c0629fbe0960cfefedcbb4c7226ccf9
Author: sbodin sbodin@cst.fr
Date: Thu Feb 25 11:40:13 2016 +0100

> git log  bdd_archives/canhelp2016_cannes2015_dev-2016-01-18-12h15.sql 
merge canhelp.sql


> du -sh .git
188M    .git
> du -s .git
192160  .git


> git filter-branch --tree-filter 'rm bdd_archives/canhelp2016_cannes2015_dev-2016-01-18-12h15.sql' HEAD
Rewrite 6d972138ed93bf77512687a8074e5cbe6de38249 (1/1162) (0 seconds passed, remaining 0 predicted)    rm: impossible de supprimer 'bdd_archives/canhelp2016_cannes2015_dev-2016-01-18-12h15.sql': Aucun fichier ou dossier de ce type
tree filter failed: rm bdd_archives/canhelp2016_cannes2015_dev-2016-01-18-12h15.sql
> ls bdd_archives/canhelp2016_cannes2015_dev-2016-01-18-12h15.sql
bdd_archives/canhelp2016_cannes2015_dev-2016-01-18-12h15.sql

Il fallait ajouter les options -rf à rm :

> git filter-branch --tree-filter 'rm bdd_archives/canhelp2016_cannes2015_dev-2016-01-18-12h15.sql' HEAD
Il y a 1162 fichier à réécrire

du -s .git
197016 .git

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch bdd_archives/canhelp2016_cannes2015_dev-2016-01-18-12h15.sql' --prune-empty --tag-name-filter cat -- --all

du -s .git
197832 .git

git gc --aggressive
Décompte des objets: 13380, fait.
Delta compression using up to 2 threads.
Compression des objets: 100% (13138/13138), fait.
Écriture des objets: 100% (13380/13380), fait.
Total 13380 (delta 9552), reused 2339 (delta 0)

Le dépot ainsi nettoyé peut être uploadé vers github.

Problème affichage des noms

Un grand nombre de commits sont mal affectés.

Si je cherche la liste des auteurs :

> git shortlog -e -s -n
   548  sbodin <sbodin@cst.fr>
   306  hnl-bis <hnl-bis@DESKTOP-7DKD13M>
   138  Hans-Nikolas Locher <hnlocher@cst.fr>
    95  ssalah <ssalah@e79222a6-4db9-11e1-9923-416be07c893d>
    31  Hans-Nikolas Locher (Laptop) <hnlocher@cst.fr>
    22  sbodin <sbodin@e79222a6-4db9-11e1-9923-416be07c893d>
    13  hnlocher <hnlocher@e79222a6-4db9-11e1-9923-416be07c893d>
     6  hnl-bis <hnl-bis@192.168.24.154>
     2  sbodin <sbodin@192.168.24.171>
     1  sbodin <sbodin@localhost>

On voit que la liste suivante est mal affectée :
* 306 hnl-bis hnl-bis@DESKTOP-7DKD13M
* 95 ssalah ssalah@e79222a6-4db9-11e1-9923-416be07c893d
* 22 sbodin sbodin@e79222a6-4db9-11e1-9923-416be07c893d
* 13 hnlocher hnlocher@e79222a6-4db9-11e1-9923-416be07c893d
* 6 hnl-bis hnl-bis@192.168.24.154
* 2 sbodin sbodin@192.168.24.171
* 1 sbodin sbodin@localhost

Saïd Salah said.salah@capgemini.com

https://help.github.com/en/articles/changing-author-info

#!/bin/sh

git filter-branch --env-filter '

OLD_EMAIL="your-old-email@example.com"
CORRECT_NAME="Your Correct Name"
CORRECT_EMAIL="your-correct-email@example.com"

if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_COMMITTER_NAME="$CORRECT_NAME"
    export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_AUTHOR_NAME="$CORRECT_NAME"
    export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags

Je transforme en :

#!/bin/sh

git filter-branch -f --env-filter '
# Sebastien
if [ "$GIT_COMMITTER_EMAIL" = "sbodin@localhost" -o "$GIT_COMMITTER_EMAIL" = "sbodin@192.168.24.171" -o "$GIT_COMMITTER_EMAIL" = "sbodin@e79222a6-4db9-11e1-9923-416be07c893d" ]
then
    export GIT_COMMITTER_NAME="Sebastien Bodin"
    export GIT_COMMITTER_EMAIL="sbodin@cst.fr"
fi
if [ "$GIT_AUTHOR_EMAIL" = "sbodin@localhost" -o "$GIT_AUTHOR_EMAIL" = "sbodin@192.168.24.171" -o "$GIT_AUTHOR_EMAIL" = "sbodin@e79222a6-4db9-11e1-9923-416be07c893d" ]
then
    export GIT_AUTHOR_NAME="Sebastien Bodin"
    export GIT_AUTHOR_EMAIL="sbodin@cst.fr"
fi
# Saïd
if [ "$GIT_COMMITTER_EMAIL" = "ssalah@e79222a6-4db9-11e1-9923-416be07c893d" ]
then
    export GIT_COMMITTER_NAME="Saïd Salah"
    export GIT_COMMITTER_EMAIL="said.salah@capgemini.com"
fi
if [ "$GIT_AUTHOR_EMAIL" = "ssalah@e79222a6-4db9-11e1-9923-416be07c893d" ]
then
    export GIT_AUTHOR_NAME="Saïd Salah"
    export GIT_AUTHOR_EMAIL="said.salah@capgemini.com"
fi

# Hans-Nikolas
if [ "$GIT_COMMITTER_EMAIL" = "hnl-bis@192.168.24.154" -o "$GIT_COMMITTER_EMAIL" = "hnlocher@e79222a6-4db9-11e1-9923-416be07c893d" -o "$GIT_COMMITTER_EMAIL" = "hnl-bis@DESKTOP-7DKD13M" ]
then
    export GIT_COMMITTER_NAME="Hans-Nikolas Locher"
    export GIT_COMMITTER_EMAIL="hnlocher@cst.fr"
fi
if [ "$GIT_AUTHOR_EMAIL" = "hnl-bis@192.168.24.154" -o "$GIT_AUTHOR_EMAIL" = "hnlocher@e79222a6-4db9-11e1-9923-416be07c893d" -o "$GIT_AUTHOR_EMAIL" = "hnl-bis@DESKTOP-7DKD13M" ]
then
    export GIT_AUTHOR_NAME="Hans-Nikolas Locher"
    export GIT_AUTHOR_EMAIL="hnlocher@cst.fr"
fi


' --tag-name-filter cat -- --branches --tags

Résultat des courses :

> git shortlog -e -s -n
   548  sbodin <sbodin@cst.fr>
   463  Hans-Nikolas Locher <hnlocher@cst.fr>
    95  Saïd Salah <said.salah@capgemini.com>
    31  Hans-Nikolas Locher (Laptop) <hnlocher@cst.fr>
    25  Sebastien Bodin <sbodin@cst.fr>

On recommence

Certaines branches n'avaient pas étés poussées en amont. Il faut donc tout recommencer.

Un peu de ménage dans les branches en contradictions.

Récupération des branches en local.

git pull asgard

Récupération des branches remote en local

for i in $(git branch -r|grep -v -- '->' ); do git checkout -b $(basename $i) $i; done

Lazy Loading avec Spring Boot - Suite : Sous composant Embeddable pour avoir un Proxy

23 March 2019

Un petit résumé de mes folles activités de la semaine dernière.

Pour faire suite au cas de lazy loading d'une propriété.
Tout d'abord, il fallait instrumenter le code, ce qui est fait dans une tache maven. Ensuite, il fallait rester à l'intérieur de la session, par exemple dans un objet de service.

Plutôt que d'ajouter une couche de service, la tentative de la semaine dernière était de placer le champ contenant le fichier dans un sous-objet.

Mon raisonement est que byte[] est un type brut, et que donc hibernate ne peut pas générer de proxy, alors que dans un sous-objet, oui...

CplRaw.java avec Embedded / Embeddable

package fr.cst.rd.digitalCinemaAssetHandler.entities;

import java.util.Collection;

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;

@Entity // This tells Hibernate to make a table out of this class
public class Cplraw {

	@Embeddable
	public static class CplInsideFile {
		@Lob
		@Column(name="cplfile")
		private byte[] content;

		public CplInsideFile() {
		}

		public byte[] getContent() {
			return content;
		}

		public void setContent(byte[] content) {
			this.content = content;
		}
	}

	public enum CryptedStatusType
	{
		Crypted, UnEncrypted;
	}


    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id_cplraw")
    private Integer id_cplRaw;

    @Column(name="cpluuid")
    private String cplUuid;

    @Column(name="contenttitletext")
    private String contentTitleText;

    @Column(name="contentkind")
    private String contentKind;

    @Enumerated(EnumType.STRING)
    @Column(name="iscrypted")
    private CryptedStatusType isCrypted;

    private String duration;

    @Column(name="iddisk")
    private String idDisk;

    @Column(name="barcodecst")
    private String barcodeCst;

    @Column(name="aspectratio")
    private String aspectRatio;

    @Column(name="estvisible")
    private boolean estVisible;

    @Embedded
    @Basic(fetch=FetchType.LAZY)
    private CplInsideFile cplInsideFile;


	// [... Suppression des getters/setters pour rester compact ...]

	public CplInsideFile getCplFile() {
		return cplInsideFile;
	}

	public void setCplFile(CplInsideFile cplFile) {
		this.cplInsideFile = cplFile;
	}


}


Mais c'est un echec cuisant, certes cela compile et fonctionne, mais finalement le champ cplFile est chargé dans le même select :

Hibernate: 
    select
        cplraw0_.id_cplraw as id_cplra1_0_,
        cplraw0_.aspectratio as aspectra2_0_,
        cplraw0_.barcodecst as barcodec3_0_,
        cplraw0_.contentkind as contentk4_0_,
        cplraw0_.contenttitletext as contentt5_0_,
        cplraw0_.cplfile as cplfile6_0_,
        cplraw0_.cpluuid as cpluuid7_0_,
        cplraw0_.duration as duration8_0_,
        cplraw0_.estvisible as estvisib9_0_,
        cplraw0_.iddisk as iddisk10_0_,
        cplraw0_.iscrypted as iscrypt11_0_ 
    from
        cplraw cplraw0_

Ajout d'une fonction RESTful à Application initialisée spring boot

23 March 2019

Résumé des opérations

Après plusieurs tentatives pour trouver la meilleur manière de representer les objets CPL avec la base actuelle, j'ai démarré le projet.
D'autres essais avait été conduits pour proposer une interface restful, une interface webGui, ou encore une gestion de user minimale.
J'ai pu progresser rapidement en intégrant certains de ces essais.

Idée de nom pour le logiciel

Digital Cinema Asset Manager (DCAM )
dicranum (Wiktionnaire : (1801) Du grec ancien κράνος, krános (« casque »), avec le préfixe di- (« deux »).[1] Nom formé par le bryologiste Johannes Hedwig en 1801 dans le livre Species Muscorum Frondosorum, p. 126.)

Gestion d'asset cinema numériques (GACM)
Pas trouvé.

Couche ORM

Suite aux nombreux essais de mapping d'une table contenant un blog encombrant, je choisi finalement de faire une hierarchie de classe avec une classe mère comportant tous les champs, une entité fille CplDetails pour les détails, et une entité fille avec un champ supplémentaire pour le blob. Cela permet de controler le chargement des blob avec plus d'accuité et en particulier d'éviter leur chargement pour les listages de CPL.

Couche restful

Un controleur restful est créé. Il renvoie un objet cplInfo, qui est transformé en JSON. Il faut mentionner explicitement les champs à ne pas exporter avec @JsonIgnore, comme l'id interne, par exemple.

Couche WebGui

Ajout de la dépendance de spring-webmvc, et de thymeleaf.

Creation de quelques templates. Ajout d'un controleur WebMvc, qui permet d'ajouter la liste des CPL au modèle avant d'envoyer au template.

A titre de test, autre template utilisant bootstrap-table. Il faut un callback javascript pour ajuster la création d'une cellule (https://stackoverflow.com/questions/27819208/adding-a-link-in-bootstrap-table).

Création d'une vue erreur.

Autre test utilisant datatable, dont la documentation est moins facile à utilser.

Idée pour styler :
https://bootswatch.com/

Il y a un thème darkly qui parrait très bien.

Spring Bean Annotations

17 March 2019

Références

Mvc patterns

17 March 2019

TODO prendre des notes sur les bonnes pratiques en matière de réutilisation de classes entre les couches de l'application. Ma question est : faut-il réutiliser les objets que l'on appelerait DTO dans la couche d'affichage, ou transvaser les valeurs dans des objets dédiés ?

Bibliograhie

Bugtracker décentralisé

13 March 2019

Pour récupérer le git describe comme version dans une applicaiton maven

12 March 2019

Je cherche depuis longtemps à intégrer le git describe comme version affichée en bas du GUI.

J'ai eu fait des hook en bash dans .git/hooks, mais ils ne fonctionnent évidemement pas sous windows...

Le plugin git-commit-id-plugin semble être la solution...

Le plugin génère un fichier de propriétés, sous la forme habituelle en java, ou bien en JSON.

Dans l'applicaiton, il faut encore récupérer ce fichier, et extraire la propriété, mais cela fonctionne avec le classloader, puisque le fichier est dans le classpath par défaut.

Java not free anymore

17 February 2019

TODO

Références

Deboguer un .war généré avec maven depuis Eclipse

17 February 2019

En utilisant l'option maven tomcat7:run-war, le war est compilé et lançé dans tomcat.

En substance, il faut pouvoir lancer la webapp avec les paramètres nécessaires pour la jvm et maven afin que Eclipse puisse connecter le debogeur.

Le conseil de la page est d'ajouter le plugin avec la configuration suivante :

<!-- Maven Tomcat7 plugin start -->
<plugin>
 <groupId>org.apache.tomcat.maven</groupId>
 <artifactId>tomcat7-maven-plugin</artifactId>
 <version>2.1</version>
 <configuration>
 <path>/${project.build.finalName}</path>
 <port>8080</port>
 <contextReloadable>true</contextReloadable>
 <systemProperties>
 <JAVA_OPTS>-Xms256m -Xmx1024m -XX:+DisableExplicitGC -Dcom.sun.management.jmxremote -XX:PermSize=256m -XX:MaxPermSize=512m</JAVA_OPTS>
 <MAVEN_OPTS>-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000</MAVEN_OPTS>
 </systemProperties>
 </configuration>
 </plugin>
<!-- Maven Tomcat7 plugin end -->

Comme il se trouve que j'utilise déjà ce plugin, il dipose déjà de la configuration suivante :

      <!-- http://www.avajava.com/tutorials/lessons/how-do-i-deploy-a-maven-web-application-to-tomcat.html -->
      <!-- http://tomcat.apache.org/maven-plugin-trunk/tomcat7-maven-plugin/usage.html -->
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <url>http://localhost:8080/manager/text</url>
          <server>hnltomcat8</server>
        </configuration>
      </plugin>

La configuration devient donc :

      <!-- http://www.avajava.com/tutorials/lessons/how-do-i-deploy-a-maven-web-application-to-tomcat.html -->
      <!-- http://tomcat.apache.org/maven-plugin-trunk/tomcat7-maven-plugin/usage.html -->
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <url>http://localhost:8080/manager/text</url>
          <server>hnltomcat8</server>
           <path>/${project.build.finalName}</path>
 <port>8080</port>
 <contextReloadable>true</contextReloadable>
 <systemProperties>
 <JAVA_OPTS>-Xms256m -Xmx1024m -XX:+DisableExplicitGC -Dcom.sun.management.jmxremote -XX:PermSize=256m -XX:MaxPermSize=512m</JAVA_OPTS>
 <MAVEN_OPTS>-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000</MAVEN_OPTS>
 </systemProperties>
        </configuration>
      </plugin>

Problème, cette configuration est chargée à chaque fois.

Je place cette configuration dans un profile déclenché par la présence de debug=true.

Lazy Loading avec Spring Boot - Suite : gestion de la session

03 February 2019

La visite d'un certain nombre de liens m'a permis de comprendre pourquoi j'obtenais une erreur lors de l'accès à getFile avec le chargement lazy de cette propriété :

Le problème, est que cet accès se fait sur une instance détachée de la session...

Un objet proxy est donc chargé en lieu et place du champ, et lorsque l'on y accède depuis l'instance détaché, on déclenche l'exception.

En ajoutant une couche de service, si l'on met l'annotation @Transactionnal sur la fonction d'apppel...

public class CplrawService
{


    @Autowired
    private CplrawRepository cplrawRepository;

    @Transactional
    public Iterable<Cplraw> findCplraw(){
        return cplrawRepository.findAll();
    }

    @Transactional
    public Optional<Cplraw> findCplrawByUuid(String uuid)
    {
        Optional<Cplraw> cplrawOption = cplrawRepository.findCplrawWithUuid(uuid);
        if (cplrawOption.isPresent())
        {
            Cplraw cplraw = cplrawOption.get();
            cplraw.getCplFile(); // Juste pour faire le loading dans la transaction

        }
        return cplrawOption;
    }

}

... on peut charger une instance de Cplraw avec les autres propriétés.

Hibernate:
    select
        cplraw0_.id_cplraw as id_cplra1_0_,
        cplraw0_.aspectratio as aspectra2_0_,
        cplraw0_.barcodecst as barcodec3_0_,
        cplraw0_.contentkind as contentk4_0_,
        cplraw0_.contenttitletext as contentt5_0_,
        cplraw0_.cpluuid as cpluuid7_0_,
        cplraw0_.duration as duration8_0_,
        cplraw0_.estvisible as estvisib9_0_,
        cplraw0_.iddisk as iddisk10_0_,
        cplraw0_.iscrypted as iscrypt11_0_
    from
        cplraw cplraw0_
    where
        cplraw0_.cpluuid=?

Lorsque l'on appelle getFile, un nouvel appel à la base de donnée est effectué.

Hibernate:
    select
        cplraw_.cplfile as cplfile6_0_
    from
        cplraw cplraw_
    where
        cplraw_.id_cplraw=?

Et il n'y a pas d'erreur...

Donc, question sanitaire sur l'usage des entitées.

Par contre, risque de ne pas être simple à gérer depuis un service restfull, puisqu'il faudra prévoir dans quel cas on doit fournir le blob de la CPL, et dans quel cas ce n'est pas la peine.

Références

J'ajoute par la suite une analyse vraiment intéressante :

RestFul Design

28 January 2019

Références

Lazy Loading avec Spring Boot

27 January 2019

Parmis les tables utilisées pour travailler sur les assets cinéma, il y a une table CplRaw dans laquelle un certain nombre d'information sont extrait, et un blob stocke le fichier XML à proprement parler.

Ce travail se fait dans le cadre d'une mise en oeuvre avec Spring Boot.

L'idée est de ne pas charger le contenu du blob en mémoire lorsque l'on fait de la récupértion de listes de CPL, que ce soit l'ensemble de la table ou un sous-ensemble filtré sur un critère.

Il faut donc charger de manière différée le blob, lorsque l'on en a besoin.

Une recherche montre au moins trois méthodes pour y parvenir :

  • Chargement Lazy de la propriété
  • Association de la table avec elle même en oneToOne
  • Création d'une hiérarchie avec une classe ne comportant que les les paramètres légers, et une classe fille ajoutant le champs correspondant au blob.

Chargement Lazy de la propriété

Le mécanisme de chargement à la demande est prévu, avec l'annotation @Basic.

Extrait de la classe CplRaw avec les options Lazy activées.

...
    @Column(name="cplfile")
    @Lob
    @Basic(fetch=FetchType.LAZY)
    private byte[] cplFile;
...

Le problème rencontré pour cette solution, est que même si la propriété est étiquettée Lazy, elle est chargée systématiquement avec les autres propriétés, ce qui pose problème pour les listes et autres findAll.

Si l'on active l'affichage des requêtes SQL, on voit que les blobs sont récupérées à travers le même select.

Hibernate:
    select
        cplraw0_.id_cplraw as id_cplra1_0_,
        cplraw0_.aspectratio as aspectra2_0_,
        cplraw0_.barcodecst as barcodec3_0_,
        cplraw0_.contentkind as contentk4_0_,
        cplraw0_.contenttitletext as contentt5_0_,
        cplraw0_.cplfile as cplfile6_0_,
        cplraw0_.cpluuid as cpluuid7_0_,
        cplraw0_.duration as duration8_0_,
        cplraw0_.estvisible as estvisib9_0_,
        cplraw0_.iddisk as iddisk10_0_,
        cplraw0_.iscrypted as iscrypt11_0_
    from
        cplraw cplraw0_
    where
        cplraw0_.cpluuid=?

On peut constater que la colone cplfile est chargée.

Pour que le chargement soit effectif, il faut activer l'instrumentation du byte code.
Cela se fait en ajoutant quelques lignes dans le .pom.

La manipulation est documentée dans le post suivant :

On peut activer l'instrumentation avec les lignes suivantes dans le .pom :

<plugin>
    <groupId>org.hibernate.orm.tooling</groupId>
    <artifactId>hibernate-enhance-maven-plugin</artifactId>
    <version>${hibernate.version}</version>
    <executions>
        <execution>
            <configuration>
                <failOnError>true</failOnError>
                <enableLazyInitialization>true</enableLazyInitialization>
            </configuration>
            <goals>
                <goal>enhance</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Au chargement :

2019-01-27 12:13:12.164  INFO 5772 --- [           main] o.h.tuple.entity.EntityMetamodel         : HHH000157: Lazy property fetching available for: fr.cst.rd.digitalCinemaAssetHandler.entities.Cplraw

Ce que l'on obtient dans la trace hibernate change :

Hibernate:
    select
        cplraw0_.id_cplraw as id_cplra1_0_,
        cplraw0_.aspectratio as aspectra2_0_,
        cplraw0_.barcodecst as barcodec3_0_,
        cplraw0_.contentkind as contentk4_0_,
        cplraw0_.contenttitletext as contentt5_0_,
        cplraw0_.cpluuid as cpluuid7_0_,
        cplraw0_.duration as duration8_0_,
        cplraw0_.estvisible as estvisib9_0_,
        cplraw0_.iddisk as iddisk10_0_,
        cplraw0_.iscrypted as iscrypt11_0_
    from
        cplraw cplraw0_
    where
        cplraw0_.cpluuid=?

Par contre, si l'on appelle la propriété, on rencontre une erreur :

...
Caused by: org.hibernate.LazyInitializationException: Unable to perform requested lazy initialization [fr.cst.rd.digitalCinemaAssetHandler.entities.Cplraw.cplFile] - no session and settings disallow loading outside the Session
	at org.hibernate.bytecode.enhance.spi.interceptor.Helper.throwLazyInitializationException(Helper.java:164)
...

Dans le contexte, les beans sont appelés depuis une application ligne de commande, sans qu'un service web soit appelé, peut-être est-ce pour cela qu'il n'y à pas de session ?

Association de la table avec elle même en oneToOne

Cela fonctionne, mais oblige à des acrobatie lors de la création d'un enregistrement. Il enregistrer une première fois la ligne sans le blob, charger de nouveau l'entité, puis y ajouter le blob.

Classe fille avec propriété supplémentaire pour le blob

Des indications sont données dans le même post que pour le lazy loading de la propriété dans la partie "Fetching subentities".

Dans mon cas, cela oblige on obtient une hiérachie de classe, avec du coup des opérations plus propres.

Une super classe commune avec tous les attributs :

@MappedSuperclass
public class Cplraw
{
... // Tous les champs et getters/setters non montrés
}

Une classe entité sans attributs supplémentaires :

@Entity
@Table(name="cplraw")
public class CplInfo extends Cplraw
{

}

Et une classe fille avec le blob :

@Entity
@Table(name="cplraw")
public class CplFile extends Cplraw
{

	@Lob
	@Column(name="cplfile", columnDefinition = "BLOB")
	private byte[] file;



	public byte[] getFile() {
		return file;
	}

	public void setFile(byte[] file) {
		this.file = file;
	}

}

Autres changements dans le code...

Il faut modifier le CplRaw repository pour mettre des types CplInfo, puisque c'est elle l'entité... Pour accéder a blob, il faut récupérer un CplFile d'une nouvelle classe CplFileRepository.

Avantages inconvénients

L'avantage de l'approche est que l'on peut clairement controler la récupération d'objets permettant d'accéder à la propriété ou pas selon le contexte.
Inconvénient : perte de transparence dans le code, puiqu'il s'agit de la même abstraction dans cette représentation.

Conclusions

La seul façon de faire qui me satisfasse en terme de sémantique est celle qui ne fonctionne pas...

Autres références

Migration de dotclear sur RPi 3 B+ avec raspbian stretch

05 January 2019

Installation d'une distribution récente

Une première tentative d'utilisation de la carte comportant une installation de raspbian wheezy avec apache2/mysql/php et dotclear d'installé. Cette carte ne peut pas booter sur un RPi 3 B+, car le processeur est trop récent et n'est géré que depuis peu par une la distribution.

L'upgrade une version plus récente n'est pas recommandé par raspbian (TODO ref).

Par ailleurs, la distribution actuelle étant wheezy, il faudrait faire des dist-upgrade vers jessie, puis vers stretch.

Installation de raspbian stretch sur la carte SD, après vérification du fichier zip et décompression.

$ lsblk # Pour repérer le device représentant la carte
$ umount /dev/sde1 /dev/sde2 # Eventuellement, démonter les partitions (mais pas le disque !)
$ sudo dd if=2018-11-13-raspbian-stretch-full.img of=/dev/sde bs=4M conv=fsync status=progress

Note, l'utilisation de Win32DiskImager reste à évaluer, à priori, cela ne permet pas de sauvegarder tout le disque, et etcher permet de copier une image vers une carte, mais pas l'inverse.

Pour un premier essai, une carte de 8Go à été utilisée.

Pour un second essai, la carte 16Go SanDisk à été utilisée.

Démarche erronée : utiliser gparted pour redimensionner la partition. Cela rends la carte défectueuse, et le RPi est bloqué au boot.
Au premier boot de la carte, celle-ci se décompresse automatiquement à la taille de la carte.

Migration de dotclear

Archivage des répertoires de dotclear sur l'ancien raspberry.

$ tar czvf dotclear-20190105.tar.gz dotclear2-10
$ mysql -u dotclear -p dotclear-db > dotclear-20190105.tar.gz

Sur le RPi 3 B+ avec Raspbian Stretch :

$ sudo mysql -u root -p # mot de passe vide, mais il faut être root

Ensuite il faut taper les instructions suivantes :

CREATE DATABASE dotclear;
# A vérifier si nécessaire: CREATE USER 'dotclear'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON `dotclear-bd`.* TO dotclear@localhost IDENTIFIED BY 'password';
FLUSH PRIVILEGES;
SHOW GRANTS FOR USER dotclear@localhost

Il faut décompresser les fichiers et adapter les répertoires :

$ sudo mysql -u root -p < dotclear-20190105.tar.gz

$ cd /var/www/html # L'emplacemenent à changé
$ tar xzvf dotclear-20190105.tar.gz
$ chmod +770 /var/www/html -R
$ chown pi:www-data /var/www/html -R
$ chmod +777 /var/www/html/dotclear2.10/cache
$ chmod +777 /var/www/html/dotclear2.10/public
$ ln -s dotclear2-10 dotclear
$ sudo apt-get install apache2 mysql-server php php-mysql
$ sudo apt-get install php-xml # 1h de recherche

Dans le fichier inc/config.php il faut changer le driver "mysql" par "mysqli", à cause de php7.
Ce détail m'a fait perdre un temps fou. Pour avoir un affichage plus précis, on peut activer la fonction debug dans inc/config.php en ajoutant la ligne suivante :

    define(‘DC_DEBUG’, 'true');

Autre truc de dépannage, pour savoir si php est activé, créée un fichier /var/www/html/test/index.php contenant le texte suivant :

    <?php phpinfo(); ?>

Résultat

Cela fonctionne. Il reste qu'on peut maintenant migrer vers dotclear 2.14, mais l'installation automatique ne focntionne pas.

Références

Notes incomplètes sur un problème avec Eclipse

05 January 2019

Pbm marbero

Avant :
Target Platform Definition DSL and Generator
http://mbarbero.github.io/fr.obeo.releng.targetplatform/p2/latest/

Remplacer ?

Target Platform Definition DSL and Generator
https://github.com/eclipse-cbi/targetplatform-dsl

Ticket annonçant le changement :
https://bugs.eclipse.org/bugs/show_bug.cgi?id=538414

http://download.eclipse.org/mpc/releases/1.7.0

Utisation de swagger pour documenter service restful en Spring Boot

20 November 2018

Exemple d'utilisation :

Limitation des filesystems supportés par Dropbox

14 November 2018

Sur mon poste de travail au boulot (OpenSUSe Leap 15), j'ai du déplacer mon /home vers une nouvelle partition ext4.

Sur la machine de la maison, une Ubuntu 18.04.1 LTS (bionic), Dropbox n'est pas content et affiche un message d'erreur, alors que la partition du home est en ext4.

En cherchant un peu, il faut un peu plus que de l'ext4 :

Le dossier Dropbox doit également se trouver sur un disque dur ou une partition au format ext4.

ecryptfs n'est pas pris en charge, mais la synchronisation se poursuit dans Dropbox avec les systèmes de fichiers acceptés qui font l'objet d'un chiffrement de disque complet (par exemple, LUKS).

L'utilisation de cryptfs est peut-être en cause...

$ ps aux |grep crypt
root        44  0.0  0.0      0     0 ?        I<   18:57   0:00 [crypto]
root        55  0.0  0.0      0     0 ?        S    18:57   0:00 [ecryptfs-kthrea]
The ext4 file system needs to be formatted with ext_attr on. This is the default behavior, but you can confirm by running debugfs -R features /dev/sda1 (or whatever your device file is called -- if you're using LVM it might be something like /dev/mapper/computername--vg-partitionname)

The ext4 partition needs to be mounted with the user_xattr option set (You can check for and add the option in GNOME disks or edit /etc/fstab directly)
The target folder (or Dropbox sync folder) needs to be at least two levels beneath the mountpoint, as described in this post. This is obviously a facepalm-worthy bug.

Utilisation de BaseX avec des requetes FLWOR

12 November 2018

Notes robotique août 2018

30 August 2018

L'objectif des tests de cet fin d'été était de tester l'utilisation de firmata, reperé lors de précédentes sessions de travail sur arduino.

Mise en oeuvre de Firmata

Firmata est un "firmware", c'est à dire qu'il s'installe comme programme résident du microcontroleur, il fait interface avec les différents ports et envoi les changements d'états et reçoit les ordres de changement via un protocole.

Il peut être piloté par un logiciel client qui implémente le protocole.

L'inconvénient est qu'il faut laisser le montage branché en USB et que l'intelligence est coté ordinateur, d'ou moins de réactivité du fait du temps de latence. Ca ne convient pas à tous les usages.

Mise en place de Firmata coté Arduino

Pour mettre en place le firmware, rien de plus simple.

Il faut ajouter l'utilisateur courant à dialout, mais dans les versions récentes de arduino, si ce n'est pas le cas un popup graphique propose de le faire en saisisant le mot de passe root. Il faut se déconnecter/reconnecter de la session pour que ce soit pris en compte.

$ sudo apt-get install arduino
$ arduino

Ensuite ouvrir le sketch Firmata Standard via Fichiers/Examples/Firmata/StandardFirmata et l'envoyer vers l'arduino.

A noter que la gestion des librairies à changé dans le logiciel, il n'y a pas la gestion de version de librairie dans la version 2:1.0.5 que j'utilise sur Ubuntu LTS 18.04. Il n'y a pas d'option "Manage library" sous l'onglet sketch.

Il est tout de même possible d'utiliser une nouvelle version de la lib Firmata en procédant comme ceci :

$ cd temp
$ git clone https://github.com/firmata/arduino.git
$ cd arduino
$ bash relase.h
$ ls *.zip
Arduino-1.0.x-Firmata-2.5.8.zip  Firmata-2.5.8.zip

Deux fichiers zip sont générés. Il faut faire Sketch/Add Library pour ajouter Arduino-1.0.x-Firmata-2.5.8 dans ~/sketchbook/libraries/Firmata, que j'ai renomé ~/sketchbook/libraries/Firmata25 pour éviter le confusions.

On peut alors charger le sketch dans dans sketchbook/libraries.

Utilisation de Snap4Arduino

Permet de commander l'arduino en utilisant le protocole Firmata.

Pour l'installation du logiciel, j'ai téléchargé la version linux.

Il faut détarer, puis :

$ cd local/Snap4Arduino_desktop-gnu-64_1.2.6/
$ ./launcher.sh

Utilisation de firmata4j

Prise en charge classique par maven.

A noter il faut changer une des dépendances pour faire taire l'avertissement sur les logs.

Alimentation de l'arduino pour utilisation du kit relais veleman

Le Kit relais demande un peu plus de puissance electrique. Il faut donc alimenter l'arduino.

Quelques articles pour détailler comment alimenter l'arduino :

En bref : utiliser une alimentation stabilisée entre 9V et 12V sur le jack DC2.1.

La spec de veleman indique plutot 12V.

Pour ce qui me concerne, l'utilisation de l'alimentation de mon chargeur de batteries donne toute satisfaction pour tester le kit velleman, qui fonctionnne par ailleurs très bien avec le firmware firmata.

Montages de tests

Je reprend les montages classiques de arduino, diode, potard, interupteur.

On peut retrouver ces montages sur le cours de OpenClassRoom, par exemple.

A noter le montage de l'interupteur nu en positionnant l'entrée en "PULLUP".

Kit Moteur AdaFruit

Alimentation pour le kit moteur

L'utilisation du 12V sur l'arduino ne fonctionne pas (bien sur, j'ai vérifié la présence du cavalier Vin).

En revanche, en supprimant le cavalier Vin et en utisant le 12V du transfo directement sur l'entrée de la carte moteur, j'ai pu faire fonctionner le moteur stepper.

Des contributions existent pour inclure le shield dans Fritzing :

Installation de Fritzing pour faire des schémas

Il y a un bug dans l'installation sur ubuntu, les parts ne sont pas installées par défaut :

$ sudo apt-get install fritzing
$ sudo apt-get install fritzing-parts

Mais en plus il y a un bug de localisation :
Il faut ajouter un lien symbolique :

# cd /usr/share/ 
# ln -s fritzing/parts/ fritzing-parts

Du détail sur les discussions autour de ce bug :
* GitHub fritzing/fritzing-app : 0.9.3 not finding parts under Ubuntu 17.10 #3386
* GitHub fritzing/fritzing-app : Making life difficult for package maintainers #3286

Produits pour arduino

Autres resources idées, docs

Spring Boot comme alternative légère à Spring

26 July 2018

Dans la derniere vesion de Spring, est apparu un framework de développement rapide nommé Spring Boot.

Ce framework, rend facile l'élaboration d'applications en permettant d'instrumenter du code avec des annotations. Les applications sont complètement configurés avec des adaptateurs par défaut, ce qui permet d'une par un résultat rapide, et d'autre part une customisation progressive possible en remplaçant au fur et à mesure des besoins les adapteurs par défaut.

Exemple d'accès aux données

L'exemple suivant :

Montre comment rendre accessible par API restful des données en base de données mysql.
Très facile à mettre en oeuvre.

L'exemple permet de lister des utilisateurs en base de donnée et d'en ajouter un.

L'exemple est très simple, il ne met pas en oeuvre de sécurité. Il utilise JPA pour accéder à la couche de données.

Dans les critiques que l'on peut formuler, celui d'utiliser une méthode GET pour ajouter des données, ce qui n'est pas très restful.

Je l'adapte au données de canhelp, ce qui demande quelques adaptations pour gérer le champ profil comme une énumération.

Parmis les modifications effectués, je change le comportement de la classe User, qui sert de bean pour l'entity.

    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer iduser;

par

    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer iduser;

En effet, dans le cas particulier de MySQL, si on utilise AUTO, la couche d'accès aux données créé une table hibernate_sequence.

La table comprend simplement une valeur, incrémentée à chaque creation de ligne :

SELECT * FROM `hibernate_sequence`
2	

Les appels HTTP pour lister les user fonctionne :

curl 'localhost:8080/demo/all'

L'appel, modifié pour créer une nouvel user aussi :

curl 'localhost:8080/demo/add?login=hnlocher&role=monteur&profil=FIF_MIF&firstName=Hans-Nikolas&lastName=Locher&email=hnlocher@cst.fr&password=password&barcode=CST1800124&ip=192.168.24.80'

Ajout de pages web dynamiques

Je peux ajouter sans heurt, des pages statiques (index.html) et dynamique, hello.ftl, utilisant le moteur de template freemarker.

Le controleur pour accéder a la page est dans la même classe que pour l'accès à un contenu JSON. Comment se fait la discrimation ?

    @GetMapping(path="/all")
    public @ResponseBody Iterable<User> getAllUsers() {
        // This returns a JSON or XML with the users
        return userRepository.findAll();
    }
	
    @GetMapping("/hello")
    public String hello(Model model, @RequestParam(value="name", required=false, defaultValue="World") String name) {
        model.addAttribute("name", name);
        return "hello";
    }

Dans un cas, on précise que c'est le contenu du retour qu'il faut envoyer, et le framework fait le reste.

Dans le second cas, un modèle est passé en paramètre pour être configuré et le nom de la vue est renvoyé. Par défaut, le framework cherche un template correspondant à la vue.

Ajout d'une gestion de l'authentification

Je superpose à l'exemple précédent une partie de la recette présentée ici :

En particulier, il faut ajouter la section suivante dans le fichier pom.xml :

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

Pour gérer un formulaire de saisie de login mdp, il faut faire les modifications suivantes :

  • ajouter des controleurs/vues supplémentaires
  • Créer les entrées dans les controleurs
  • Préciser les droits et paramètrer la sécurité.

Cela fonctionne, on peut se connecter dans un navigateur sur la page "home", puis en cliquant, aller sur "hello", qui est intercepté par un passage au formulaire de la page "login". Ensuite, on reçois un hello customizé avec le nom du user.

Ce qui ne marche pas :

  • Les users sont configuré en dur, et la base des users utilisés pour l'authentification est en mémoire
  • Pour les appels à la partie restful, cela ne fonctionne pas, on ne peut plus acceder.

La securité semble gérer par des cookies de session, ce qui explique sans doute qu'en l'absence d'un token, ce ne puisse pas fonctionner avec curl.

Un autre exemple bien documenté :

Sinon, un peu de littérature :

A déplacer : Bibliographie curl

A déplacer dans une autre note : Concernant l'approche restFul

Exemple d'API

Programmes au démarrage sur Ubuntu

16 July 2018

Sur Ubuntu 18.04, pour éditer les programmes au démarrage, il y a un programme graphique : gnome-session-properties.

Une partie est matérialisée par des fichier .desktop dans ~/.config/autostart.

Par exemple, dans l'interface graphique, kerneloops-applet apparait dans la liste des commandes lançées aux démarrage, mais il n'y a pas de fichier correspondant dans .config/autostart/.

D'après la spécification de freedesktop.org, on peut aussi trouver des démarrages pour l'ensemble du système dans /etc/xdg/autostart/ (qui sera moins prioritaire qu'un fichier de nom identique dans le home).

Les répertoires utilisées peuvent être configurés avec les variables d'environement XDG_CONFIG_HOME et XDG_CONFIG_DIRS .

Mise à jour de Eclipse vers Oxygen

04 July 2018

Scénario de base

La migration de mon installation de Eclipse sur mon portable est bloquée : j'échoue à chaque fois à passer de Neon 3 (4.6.3) à Oxygen (4.7.0).

La raison : une dépendance non résolue.

Un dialogue s'ouvre en parallèle de la fenetre principale :
Some sites could not be found. See the error log for more detail.

No repository found at http://download.eclipse.org/tools/mylyn/update/extras.
No repository found at http://download.eclipse.org/tools/mylyn/update/weekly/extras.

Un composant est en erreur :

Cannot complete the install because one or more required items could not be found.
  Software being installed: m2e - Maven Integration for Eclipse (includes Incubating components) 1.9.0.20180606-2036 (org.eclipse.m2e.feature.feature.group 1.9.0.20180606-2036)
  Missing requirement: org.eclipse.m2e.binaryproject 1.9.0.20180606-2036 requires 'bundle org.eclipse.jdt.launching 3.10.0' but it could not be found
  Cannot satisfy dependency:
    From: m2e - Maven Integration for Eclipse (includes Incubating components) 1.9.0.20180606-2036 (org.eclipse.m2e.feature.feature.group 1.9.0.20180606-2036)

Tentatives de contournement diverses

Suppression de la liste des dépots indiqués en erreur :

Je supprime les modules m2e, mais eclipse me propose de les réinstaller au démarrage suivant.

Nouvelle tentative d'update, je rencontre les mêmes problèmes.

Je fais quelques supressions de packages et de fonctionnalités, comme Camunda

Toujours pas d'amélioration de la situation.

Quelques liens

Comment faire facilement des schémas UML ?

06 June 2018

Petits schémas UML

Pour arreter de faire des petits schémas moches dans OpenOffice, avec les galère d'improtation dasn un doc etc...

J'utilisais une extension google documents (WebSequenceDiagrams) permettant de définir avec un langage simple, type markdown un diagramme de séquence et de le convertir à la volée en diagramme. Mais l'extension est vieillissante.

PlantUML

Et puis j'ai découvert le langage PlantUml qui permet de faire des diagrammes de séquences, mais bien d'autres diagrammes aussi.

Exemple :

@startuml
Alice -> Bob: Authentication Request
Bob --> Alice: Authentication Response

Alice -> Bob: Another authentication Request
Alice <-- Bob: another authentication Response
@enduml

Ajout le 2019-12-01 :

Autres frameworks

Le framework Gravizo permet d'afficher un diagramme svg à partir de schémas rédigés en PlantUml, ou en dot, on en umlGraph.

A partir de l'exemple ci-dessus :

Un volume LVM full, que faire

08 March 2018

Problème affichage des icones dans LibreOffice

04 March 2018

Les icones n'apparraissaient pas correctement dans LibreOffice, depuis mon passage à la distribution 17.10.

J'ai installé tous les thèmes d'icones :

$ sudo apt install libreoffice-style-*

Ensuite j'ai en allant dans Outils>Options, puis sur l'onglet LibreOffice, sous-onglet Affichage, dans la liste déroulante Style d'icônes, j'ai choisi Sifr Dark.

Validation de schema XML

27 February 2018

xmllint

Le conseil qui revient souvent est xmllint :

xmllint --schema schema.xsd undertest.xml

Lorsque certaines dépendances de schema ne sont pas disponibles, il y a une erreur.

Le problème est que xmllint ne parviens pas à récupérer les sources du schéma pour la signature, et échoue à faire la validation.

Ce qui est surprenant, c'est que le répertoire contient un fichier catalog.xml qui contient la correspondance

    <public publicId="http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema" uri="xmldsig-core-schema.xsd"/>
    <system systemId="http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd" uri="xmldsig-core-schema.xsd"/>

xmlbeans

En reprenant de vieux scripts à moi de 2008, je retombe sur la commande validate, qui est un script de wrapping pour un appel à une bibliothèque de parsing java, xmlbeans.

Même problème de dépendances de schéma avec la commande par défaut :

> export XMLBEANS_LIB=$(dirname $(find /usr -iname xbean.jar 2>/dev/null))
> validate xsd/SMPTE-430-1-2006-Amd-1-2009-KDM.xsd KDM_BATMAN-GOTHAM-BY-GASLIGHT_FTR-1_EN-FR_51_HD_WR_20180124_DUB_OV_A1613.xml 
Schema invalid:
Loading referenced file file:/path/to/file/xsd/SMPTE-430-3-2008-ETM.xsd
/path/to/file/xsd/SMPTE-430-1-2006-Amd-1-2009-KDM.xsd:11:3: error: Could not load resource "http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd" (network downloads disabled).
/path/to/file/xsd/SMPTE-430-3-2008-ETM.xsd:6:3: error: Could not load resource "http://www.w3.org/TR/2002/REC-xmlenc-core-20021210/xenc-schema.xsd" (network downloads disabled).
/path/to/file/xsd/SMPTE-430-3-2008-ETM.xsd:14:7: error: src-resolve: element 'Signature@http://www.w3.org/2000/09/xmldsig#' not found.
/path/to/file/xsd/SMPTE-430-3-2008-ETM.xsd:25:7: error: src-resolve: type 'X509IssuerSerialType@http://www.w3.org/2000/09/xmldsig#' not found.
/path/to/file/xsd/SMPTE-430-3-2008-ETM.xsd:47:7: error: src-resolve: element 'EncryptedKey@http://www.w3.org/2001/04/xmlenc#' not found.
/path/to/file/xsd/SMPTE-430-3-2008-ETM.xsd:48:7: error: src-resolve: element 'EncryptedData@http://www.w3.org/2001/04/xmlenc#' not found.
/path/to/file/xsd/SMPTE-430-1-2006-Amd-1-2009-KDM.xsd:20:13: error: src-resolve: type 'X509IssuerSerialType@http://www.w3.org/2000/09/xmldsig#' not found.
/path/to/file/xsd/SMPTE-430-1-2006-Amd-1-2009-KDM.xsd:56:13: error: src-resolve: type 'DigestValueType@http://www.w3.org/2000/09/xmldsig#' not found.

Pour résoudre le problème :

> validate -dl xsd/SMPTE-430-1-2006-Amd-1-2009-KDM.xsd KDM_BATMAN-GOTHAM-BY-GASLIGHT_FTR-1_EN-FR_51_HD_WR_20180124_DUB_OV_A1613.xml

L'option -dl autorise à télécharger les schémas en ligne, ce qui permet de résoudre la dépendance à la definition du schéma des signatures.

schema-check

Récupération du code de schema-check dans l'annexe C.3.1 du DCI 1.1.

Installation des dépendances :

sudo apt-get install libxerces-c-devel
g++ schema-check.cpp -o schema-check -lxerces-c


A fouiller

Use local resources when validating XML

Comment gérer les tags ?

26 February 2018

BTRFS partitions are full

26 February 2018

Mauvaise surprise ce matin : blocage en plein zypper install, le disque est plein et j'ai une erreur pour acceder aux fichiers de la base de données RPM.

C'est tout le problème des snapshots BTRFS : ils prennent de la place.

Pour lister les snapshots : snapper list, ensuite on peut les effacer par numéro :

Pour faire le ménage, il faut effacer des snapshots, par exemple :

 for i in `seq 1 3656`; do snapper delete $i; done

Pour ce qui me concerne, j'ai effaçé les snapshots notés 'important=no'

Je n'ai libéré que 2% de la partion.

Références

Banques de son

23 February 2018

A la recherche de sons.

Pour mes premiers essais dans Ardour, je cherche des sons pour faire des virgules ou des illustrations.

ToDo Travaux

21 February 2018

TODO

Garage

  • Serrure garage ou changement de porte
    • mesure porte :
  • Crochet vélo ?
  • Goutiere du garage
  • Recupérateur d’eau
    • poser un morceau de goutière PVC avec un support distant du mur
    • Insérer le filtre dans la goutière descendante
  • Cablage électrique du garage

Extérieur

  • Maçonnerie portail
  • Serrure portail
  • Composteur
  • Peinture façade
  • Inclinaison marches terrasse
  • Démontage antenne et parabole
    • Conditionné à l'utilisation d'une echelle de 10m
  • Peinture mur
  • Recimenter le gratte-botte

Intérieur

  • Etagere angle Hector
  • Poignées portes Pax buanderie
    • Ecart 96mm
  • Etagère au dessus de la machine à laver
    • TODO mesures
  • Etagère sous l'escalier
    • TODO mesures
  • Isolation porte du bas
    • Dimension porte : 79x196
  • Dépoli pour toilettes
    • Dimension verre libre : 39x81,8
  • Mousse pour lave-vaisselle
  • Detecteurs de fumée
  • Etagère toilettes
    • 28x77x107
  • Barre de seuil porte rdc descente ss au moins 6x4x71

Achat

Fait

  • Ajouter les rayons dans la PAX chambre parents
  • Réparation va-et-vient escalier et changements interupteurs
  • Accrocher la sonnette
  • Resceller prise Félix
  • Montage meuble blanc sous-bureau
  • Decheterie pour le vert
  • Etendage 4 niveaux
  • feuille platique pour livres escalier
  • Commutateur/va-et-vient Schneider Electric * 5
  • Reglette pour cacher cable réseau.
  • Installation étagère 40+porte+déplacement interrupteur
  • Etabli avec planche et tréteaux hauts
  • Luminaire buanderie < 8,5cm
  • Mousse toit garage
    • Grattage fait
    • Produit Axton indisponible
  • boites lit Félix
    • 129*19,5
  • Ajouter rayons etagere bleue garage
  • Sonnette en 240V, il n'est pas possible de tirer un cable
  • Porte coulissante pour le salon
  • Achat Taille-haie

Quelques détails sur la syntaxe md dans jBake

19 February 2018

Je m'aperçois que la syntaxe reconnue dans les fichiers md par jBake, n'est pas exactement celle à laquelle je suis habitué.

Un des problèmes venait du fait qu'il faut mettre une ligne vide avant la syntaxe '```'.

Fonctionnement de jBake pour les templates et les sources

jBake utilise What is Apache FreeMarker, de la fondation Apache, un moteur de template.

Pour l'interpretation du markdown, jBake utilse pegdown.
A noter sur le github de pegdown :

:>>> DEPRECATION NOTE <<<:

Les concepteurs de jBake envisagent donc de changer de bibliothèque pour interpreter le md.

Divers liens autour de ce propos

Dans le post de forum suivant, un utilisateur fait le bilan des metadonnées qui manquent dans jBake, par rapport à Jekyll.

Projet de porte coulissante

19 February 2018

Initiation à Ardour

19 February 2018

Pour linux, un site (un peu daté) : comporte pas mal d'informations :

Un tutoriel, mais pour Adour 4 :

Une initiation à Ardour très courte est donnée ici :

Mais c'est un peu trop court pour comprendre comment déplacer la timeline, comme interagir avec les différents outils.

Home Cinema

10 February 2018

Cobra

Les numériques

Son-Video

Divers

Home Casting

10 February 2018

Mise en place d'un plugin JBake pour maven

07 February 2018

Mise en place du plugin Ingenieux

Je suis [JBake Maven Plugin Walkthough] pour expliquer le fonctionnement du plugin

Il faut forker le [jbake-sample] sur github.

L'exemple qui est donné est inutilment complexe, puisqu'il fail l'hypothèse d'un déploiement dans S3.

Je peux supprimer une bonne partie du code donnée dans l'exemple.

On obtient l'erreur suivante :

java.lang.Exception: Error: Source folder must exist!

On peut la supprimer en ajoutant ceci :

    <inputDirectory>${project.basedir}</inputDirectory>
    <outputDirectory>${project.build.directory}/site/</outputDirectory>

Il reste l'erreur suivante à la generation :

No template engine found for template: archive.ftl
[...]
config.title

Des exemples plus simples sont donné dans [Integration of JBake in Maven – Static Websites].

Ajout de la section suivante :

    <dependencies>
        <!-- for freemarker templates (.ftl) -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.20</version>
        </dependency>
    </dependencies>

En ajoutant des dépendances dans le plugin, il n'y a plus d'erreur pour trouver le template engine.

Par contre :

[ERROR] Rendering index [C:\Users\hnlocher\Desktop\EssaiJBake\target\site\index.html]...failed!
org.jbake.template.RenderingException: freemarker.core.InvalidReferenceException: The following has evaluated to null or missing:
==> content.rootpath  [in template "index.ftl" at line 18, column 56]

Tip: If the failing expression is known to be legally null/missing, either specify a default value with myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthessis: (myOptionVar.foo)!myDefault, (myOptionVar.foo)??

Le problème semble être que la variable ${content.rootpath} n'est pas correctement initialisée. La Documentation JBake 2.5.1 - section global indique qu'il s'agit d'une variable normalement toujours disponible pour les templates.

Dans l'extrait du template suivant :

    <p>Older posts are available in the <a href="${content.rootpath}${config.archive_file}">archive</a>.</p>

Références

Mise en place du plugin Blazeit

https://github.com/Blazebit/jbake-maven-plugin

Il faut déplacer les sources, car le plugin à l'air sourd au paramètres lui indiquant les sources :
jbake.inputDirectory
jbake.outputDirectory

Erreur : jbake [WARNING] Unable to load a suitable rendering engine for extensions [md]

Je trouve la réponse ici :

En fait, le problème venait du fait que le plugin de conversion du markdown doit
être explicitement chargé.

Il faut donc ajouter la dépendance suivante dans le fichier pom.xml.

<dependency>
  <groupId>org.pegdown</groupId>
  <artifactId>pegdown</artifactId>
  <version>1.4.2</version>
</dependency>

Présentation du format "Cinema Mezzanine"

07 February 2018

Introduction

Le format "Cinema Mezzanine" est un format appartenant à une famille de format plus large : l'IMF, pour Interoperable Master Format.
Il a été proposé initialement comme appendice de la recommandation RT-021 par le groupe de travail CST en 2013, avant de suivre le processus de standardisation SMPTE et être publié en 2016 sous la référence SMPTE ST 2067-40.

IMF

L'IMF est connu aujourd'hui pour une autre de ces applications, l'application 2 étendue, "Studio master", utilisée en particulier comme format de livraison par Netflix.

L'IMF reprend l'architecture de fichiers du DCP et en étend les possibilités. L'image, le son et les sous-titres sont stockés dans des fichiers MXF séparés, que l'on appelle les essences.
Une CPL, pour compositiion playlist, permet d'indiquer l'assemblage à effectuer pour reconstituer à la lecture ou à la conversion, dans quel ordre lire les différents éléments de manière à ce que chaque essence reprenne sa place dans le media final. Ce fichier est au format XML, et peut être facilement consulté, même sans éditeur spécialisé.
Le format IMF reprend aussi la notion de package, c'est à dire une unité de livraison, comprenant un certain nombre de fichiers, que ce soit des CPL ou des essences.
Un des principaux avantage de ce format est de pouvoir faire de la livraison incrémentale. Par exemple, on peut livrer un package comprenant le master de la version originale d'un film, avec les éléments son de version originale, l'image. Un deuxième package pourra être livré avec une CPL référençant des assets du premier package, sans les livrer à nouveau. On pourra donc ne transmettre que le son en français et les images comportant des titres, sans retransmettre l'intégralité.
L'IMF introduit un nouveau type de fichier, qui n'éxiste pas dans le DCP : l'OPL. Ce fichier permet de guider la conversion du master contenu vers un fichier lié à un besoin précis. Il peut par exemple piloter le transcodage vers un master de diffusion télévisuelle avec des bandes noires ou du pan & scan, vers l'espace couleur de diffusion et les canaux audio nécessaires.

Cette façon de travailler est source de cohérence et d'économie de bande passante.

L'ensemble des spécifications de l'IMF sont maintenues par la SMPTE. Un jeu de standard défini les "core constraints", c'est à dire le socle commun au format, et il y ensuite un standard par application pour spécialiser les contraintes en fonction du domaine d'application.

L'application la plus utilisée aujourd'hui est l'IMF application 2 étendue, qui est demandée par Netflix. Il est à noter que l'entreprise dispose d'un cahier des charges très complet qui spécialise encore un peu plus les contraintes pour les besoins spécifiques de l'entreprise.

L'application 4 - Cinema Mezzanine

Le format Cinema Mezzanine est destiné à transporter des master haute qualité pour les films cinématographiques. Les contraintes ont été choisies pour assurer la qualité et éviter les ambiguités.
Les images 24,4K ou 8K peuvent être stockés dans des conteneurs MXF utilisant la compression lossless JPEG 2000 et un stockage des pixels sur 16 bit entiers non signés par primaire. L'espce colorimétrique retenu est le XYZ, non précompensé.

Le format est désormais implémenté par less principaux constructeurs de solutions d'encodage.

Différents problèmes sous NFS

28 January 2018

Mise en place d'un controleur supplémentaire

08 March 2017

Exemple d'un controleur HelloWorld

Création d'un fichier /src/main/java/struts/action/HelloWorldAction.java

    package struts.action;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.log4j.Logger;
    import org.apache.struts.action.ActionForm;
    import org.apache.struts.action.ActionForward;
    import org.apache.struts.action.ActionMapping;
    import org.apache.struts.actions.DispatchAction;
    
    import struts.form.HelloWorldForm;
    
    
    
    public class HelloWorldAction extends DispatchAction 
    {
        
        private static final Logger logTechnique=Logger.getLogger("logTechnique");
        
        public ActionForward init(ActionMapping mapping, ActionForm form,
                HttpServletRequest request, HttpServletResponse response) throws Exception 
        {
    
            try
            {
                HelloWorldForm helloWorldForm = (HelloWorldForm) form;
                if (helloWorldForm != null )
                {
                    logTechnique.debug("Le formulaire en paramètre n'est pas nul");
                    String azerty = request.getParameter("azerty");
                    if (azerty != null && !"".equals(azerty))
                    {
                        helloWorldForm.setName(azerty);    
                    }
                    else
                    {
                        helloWorldForm.setName("Your name");    
                    }
                    
                    
                }
                else 
                {
                    logTechnique.debug("Aucun formulaire inialisé, on en créé un");
                    helloWorldForm = new HelloWorldForm();
                    helloWorldForm.setName("Hans-Nikolas (from HelloWorldActionClass)");
                }
                
                // Initialisation des chmamps
    
                request.setAttribute("helloWorldForm", helloWorldForm);
                request.setAttribute("hnlmessage", "Message from Action init");
            
            }
            catch(Exception e)
            {
                logTechnique.debug(e);
                request.setAttribute("exception", e);
                return mapping.findForward("fault");
            
            }
            
            return mapping.findForward("etape2");
        }
        
        public ActionForward etape2(ActionMapping mapping, ActionForm form,
                HttpServletRequest request, HttpServletResponse response) throws Exception 
        {
    
            try
            {
                HelloWorldForm helloWorldForm = (HelloWorldForm) form;
                if (helloWorldForm != null )
                {
                    logTechnique.debug("Le formulaire en paramètre n'est pas nul");
                    
                    if ("Fault".equals(helloWorldForm.getName()))
                    {
                        throw new Exception("Fault throwed because name='Fault'");
                    }
    
                }
                
                // Initialisation des chmamps
    
                request.setAttribute("helloWorldForm", helloWorldForm);
                request.setAttribute("hnlmessage", "Message from Action etape2");
                
                
            
            }
            catch(Exception e)
            {
                logTechnique.debug(e);
                request.setAttribute("exception", e);
                return mapping.findForward("fault");
            
            }
            
            return mapping.findForward("etape2");
        }
    
    
    
    }

Création d'un formulaire /src/main/java/struts/form/HelloWorldForm.java

    package struts.form;
    
    import org.apache.struts.action.ActionForm;
    
    public class HelloWorldForm extends ActionForm 
    {
        
        private static final long serialVersionUID = 1L;
        
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
        
    
    }

On ajoute les nouveaux beans à la description de servlet dans /src/main/webapp/WEB-INF/action-servlet.xml

    <beans>
     ...
        <bean name="ficheForm" class="struts.form.FicheForm" />
        <bean name="extractionForm" class="struts.form.ExtractionForm" />
        <bean name="mouvementSupportForm" class="struts.form.MouvementSupportForm" />    
        <bean name="/logout" class="struts.action.LogoutAction"/>
        <bean name="/actionHelloWorld" class="struts.action.HelloWorldAction"></bean>
     </beans>

Ajout d'un form-bean de la classe HelloWorld dasn /src/main/webapp/WEB-INF/struts-config.xml. A noter le bean s'appelle helloworldForm.

        <form-bean name="ficheForm" type="struts.form.FicheForm"></form-bean>
        <form-bean name="extractionForm" type="struts.form.ExtractionForm"></form-bean>
        <form-bean name="mouvementSupportForm" type="struts.form.MouvementSupportForm"></form-bean>
        <form-bean name="helloworldForm" type="struts.form.HelloWorldForm"></form-bean>
     </form-beans>

Plus loin on ajout une action

    <action-mappings>
    ...
          <action path="/actionHelloWorld" type="org.springframework.web.struts.DelegatingActionProxy" name="helloworldForm" parameter="action">
            <forward name="init" path="helloworld" redirect="false" />
            <forward name="etape2" path="helloworld" redirect="false" />
            <forward name="fault" path="fault" redirect="false" />
          </action>   
 
    </action-mappings>

On ajoute une définition dans /src/main/webapp/WEB-INF/tiles-defs.xml

        <definition name="helloworld" page="/tiles/template.jsp">
            <put name="title" type="string" value="Hello World" />
            <put name="header" value="/tiles/top.jsp" />
            <put name="menu" value="/tiles/menu.jsp" /> 
            <put name="body" value="/ihm/helloworld.jsp" />
            <put name="bottom" value="/tiles/bottom.jsp" /> 
        </definition>
     </tiles-definitions>
</pre>

Il reste à écrire un jsp : /src/main/webapp/ihm/helloworld.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    
    <%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
    <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
    <%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
    
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <p>The begining</p>
    <h2>Hello World<logic:notEmpty name="helloWorldForm" property="name">, <bean:write name="helloWorldForm" property="name" /></logic:notEmpty> !</h2>
    
    
    <html:form action="/actionHelloWorld">
      <html:hidden property="action" value="etape2" />
      <table align="center" class="assistant">
        
          <tr>
            <td>Name : </td>
            <td>
              <html:text name="name" property="name" value="${helloWorldForm.name}" />
            </td>
          </tr>
        
        <tr>
          <td align="center">
            <html:submit>Valider</html:submit>
          </td>
        </tr>
      </table>
    </html:form>
    
    <p>The end</p>
    
    </body>
    </html>

On ajoute un lien dans le menu /src/main/webapp/tiles/menu.jsp

        <li>
            <a  href="actionUser.do?action=init">Utilisateurs</a>
        </li>
        <li>
             <a  href="actionHelloWorld.do?action=init">Helloworld</a>
        </li>

Utilisation de tomcat avec maven

08 March 2017

Utilisation du plugin tomcat7

Il convient d'ajouter la section suivante dans le pom.

      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <url>http://localhost:8080/manager/text</url>
          <server>hnltomcat8</server>
        </configuration>
      </plugin>

Le serveur indiqué dans le tag server, doit être configuré dans le fichier settings.xml de maven.

Sur windows le fichier est situé en : C:\Users\hnlocher.m2

	<settings>
		<servers>
			<server>
				<id>hnltomcat8</id>
				<username>hnl</username>
				<password>tomcat</password>
			</server>
		</servers>
	</settings>

Ensuite, en ligne de commande :

> mvn tomcat7:deploy

Références


Older posts are available in the archive.