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.
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.
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.
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.
J'avais prévu le titre "Ce qui reste à faire", mais c'est forcément illimité...
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 :
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.
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 ?
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.
Monitorer le NAS en utilisant Cacti ?
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 :
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.
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 :
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.
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.
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.
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.
21 April 2021
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 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].
12 April 2021
L'idée pincipale est de pouvoir avoir des tests configurable par fichier de configuration, et executable par maven dans un environnement container.
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.
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.
Baeldung : A Quick Guide to Spring @Value
Rappel sur la façon dont les properties peuvent être utilisées pour initialiser les beans.
Rappel fonctionnement des Rules
* Guide to JUnit 4 Rules
Baeldung : Spring Properties File Outside jar](https://www.baeldung.com/spring-properties-file-outside-jar)
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.
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.
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.
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.
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.
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"
J'aime beaucoup, par ailleurs, la représentation railroad des grammaires.
Le site bottlecap.de offre en particulier de jolies représentations.
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"
no references
referenced by:
referenced by:
referenced by:
referenced by:
::= pre-release_identifier ( '.' pre-release_identifier )
referenced by:
referenced by:
referenced by:
referenced by:
referenced by:
::= '0'
referenced by:
referenced by:
referenced by:
referenced by:
referenced by:
digit ::= '0'
referenced by:
::= '1'
| '2'
| '3'
| '4'
| '5'
| '6'
| '7'
| '8'
| '9'
referenced by:
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:
![]() |
08 February 2021
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 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 :
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.
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...
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 :
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
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.
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
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.
17 January 2021
http://blog.dmitryalexandrov.net/webassembly-for-java-developers/
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 //
.
09 January 2021
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.
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 :
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.
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.
... j'abandonne..
05 January 2021
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.
>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 ?
18 October 2020
Liste de logiciels utilisant Jack
Projet à regarder de plus près
Pour l'édition de fontes
18 October 2020
18 October 2020
Je cherche des pistes pour comprendre pourquoi l'ordinateur "trasche" parfois.
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
cat /proc/sys/vm/swappiness
Par défaut à 60
Je teste
sudo sysctl vm.swappiness=10
Pour limiter le swap...
18 October 2020
https://www.minimachines.net/actu/dissipation-raspberry-pi-4-83641
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.
Les outils de validation XML identifiés sont les suivants :
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.
$ 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)
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 :
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
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.
Avant d'aller plus loin, j'ai fait une sauvegarde de mon workspace. Il y a des fichiers en erreurs...
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.
Je réimporte tous les projets, ils apparraisent (tous ouverts...).
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/
Je copie les fichiers de
POINTmetadata/.plugins/org.eclipse.ui.workbench
dans
.metadata/.plugins/org.eclipse.ui.workbench
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
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
13 July 2020
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
$ 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
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.
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...
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.
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.
09 July 2020
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.
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.
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 :
Pour le moment, j'utilise l'ordinateur principal de la maison pour le broker MQTT et le travail de prototypage de l'adaptation.
Sur le Rpi
Sur l'ordinateur de développement
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é :
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.
Derniere aventure, n'ayant pas de résistance 220 ohm, je met en série deux résitances de 100 ohms.
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.
Les préférences ne permettent pas d'afficher un champ de saisi d'URL :
J'installe en local dans un répertoire temporaire la version XXXX.
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
Ajout d'une passerelle MQTT avec le serveur de la machine de développement.
Ajout d'un hardware "Virtual" pour pouvoir ajouter des périphériques virtuels.
On obtient la liste de hardware suivants :
On peut ajouter des périphériques virtuels, en appuyant sur le bouton, et en choisissant un type de device.
Après l'ajout d'un bouton, d'une sonde de temperature, une sonde d'humidité et une sonde combinée temperature/humidité.
Les devices sur la page d'acceuil.
La courbe de suivi de l'humidité.
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"}
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.
08 May 2020
Une part de la complexité vient du fait que l'on met ensemble plusieurs couches logicielles.
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.
Un peu con comme implem : aucune mécanisme n'est prévu pour réconcilier deux instances...
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 :
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
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'
);
06 April 2020
01 April 2020
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
Cet article montre un exemple interessant :
Autre exemple, envoyé par Marc-Antoine :
J'ai également trouvé des indications interessante dans le suivant :
https://stormpath.com/blog/build-secure-user-interfaces-using-jwts
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
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
Le français est bizarre, on dirait de la traduction automatique, mais permet de comprendre les annotations de méthodes.
31 March 2020
09 March 2020
09 March 2020
Comment reproduire un changement dans un commit d'une branche dans une autre ?
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
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...
05 March 2020
Exemple de modèle RDF : les données de
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.
OWL est une extension de RDF pour noter les ontologies.
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 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
Séparer le stockage de la représentation du modèle.
18 February 2020
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.
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.
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 :
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.
13 February 2020
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...).
To obtain the contents of an environment value when qmake is run, use the $$(...) operator.
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
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.
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.
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/
09 February 2020
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
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.
https://blog.cedrictemple.net/323-configuration-de-base-de-snmpd/
09 February 2020
La mac adresse du sonoff configuré avec le logiciel par défaut est :
83649010AA
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
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 :
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
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).
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 + RelaisCaracté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 * 25mmComment 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.
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
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.
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...
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 :
Lorsque le token ne correspond à rien
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/
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.
01 January 2020
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).
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...
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
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.
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 :
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.
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 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.
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"
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 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.
C'est le nom qui revient le plus. Facile à installer, apparement, mais peut-être moins simple à faire fonctionner d'après certains commentaires.
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
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
04 August 2019
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.
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é.
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;
On peut également utiliser l'annotation @Query, comme expliqué ici :
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).
J'ai donc créé une table Email de la façon suivante :
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
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
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é :
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.
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 :
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());
}
}
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...
29 April 2019
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
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.
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 ;
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.
The best way to map a @OneToMany relationship with JPA and Hibernate
Création d'une classe 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 :
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.
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é.
What's the difference between @JoinColumn and mappedBy when using a JPA @OneToMany association?
23 April 2019
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;
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/)
16 April 2019
07 April 2019
Dans Bootstrap 4, les fontes on disparu. On peut utiliser FontAwesome à la plage (https://fontawesome.com/icons?d=gallery).
06 April 2019
Graphviz links
https://github.com/magjac/d3-graphviz
https://bl.ocks.org/magjac/4acffdb3afbc4f71b448a210b5060bca
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
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);
}
}
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)
[...]
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.
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...
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}"
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.
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.
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 ...
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 "%r" %s %b" />
</Host>
</Engine>
</Service>
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.
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.
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.
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é).
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 .gitgit 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 .gitgit 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.
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>
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
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_
23 March 2019
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.
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é.
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.
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.
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.
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 ?
13 March 2019
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.
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.
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.
J'ajoute par la suite une analyse vraiment intéressante :
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 :
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 ?
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.
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;
}
}
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.
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.
La seul façon de faire qui me satisfasse en terme de sémantique est celle qui ne fonctionne pas...
05 January 2019
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.
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(); ?>
Cela fonctionne. Il reste qu'on peut maintenant migrer vers dotclear 2.14, mais l'installation automatique ne focntionne pas.
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
20 November 2018
Exemple d'utilisation :
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.
12 November 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.
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.
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.
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
Prise en charge classique par maven.
A noter il faut changer une des dépendances pour faire taire l'avertissement sur les logs.
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.
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".
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 :
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
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.
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'
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.
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 :
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 :
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 :
Exemple d'API
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
.
04 July 2018
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)
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.
06 June 2018
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.
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 :
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 :
08 March 2018
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.
27 February 2018
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"/>
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.
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
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.
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.
21 February 2018
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 '```'.
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.
Dans le post de forum suivant, un utilisateur fait le bilan des metadonnées qui manquent dans jBake, par rapport à Jekyll.
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.
10 February 2018
10 February 2018
07 February 2018
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>
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>
07 February 2018
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.
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.
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.
28 January 2018
08 March 2017
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>
08 March 2017
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
Older posts are available in the archive.