Migration de Joda-Time vers java.time
La migration d'un code (lire : d'un code hérité) n'est pas une partie de plaisir. Il faut énormément de planification et d'efforts pour le faire passer. Bien que ce ne soit pas le travail le plus excitant ou le plus motivant pour les développeurs, il faut de la détermination et une bonne expérience pour migrer le code existant vers les nouvelles versions de la bibliothèque. Joda-Time vers java.time est l'une de ces migrations qui nécessite une planification et une exécution méticuleuses.
Si votre projet Java a débuté avant Java SE 8, et qu'il utilise le traitement de la date et de l'heure, il a probablement utilisé Joda-Time - une excellente bibliothèque et un standard de facto pour gérer les fonctions de date et d'heure avant SE 8. Si votre projet utilise toujours Joda-Time mais que vous souhaitez migrer vers java.time, lisez ce qui suit.
La version de Java SE 8 comprenait une nouvelle API standard améliorée pour la date et l'heure, communément appelée java.time (JSR-310). Le projet Joda-Time recommande désormais de migrer vers java.time (JSR-310).
Bien que java.time (JSR-310) ait été fortement inspiré par Joda-Time, il n'est pas rétrocompatible et les concepts et terminologies ont changé. C'est pourquoi la migration de Joda-Time vers java.time nécessite une attention particulière sur chaque ligne de code que vous modifiez. Cela peut prendre beaucoup de temps et vous ferait presque regretter qu'il n'y ait pas un moyen plus facile et automatisé de migrer.
Il existe une meilleure façon de migrer et nous l'avons créée en utilisant Sensei - un plugin IntelliJ pour effectuer automatiquement des transformations de code selon les recettes (règles) que vous avez définies. Passez votre temps à définir des recettes réutilisables, plutôt que d'effectuer des tâches de migration répétitives. L'automatisation ne transformera pas seulement votre code Joda-Time hérité, mais aidera également les équipes à suivre les directives directement dans l'IDE lorsqu'elles écrivent du nouveau code.
Pour vous aider à prendre de l'avance, nous avons créé un livre de recettes public Sensei Standardization on java.time (JSR-310) qui comprend des recettes pour migrer de Joda-Time à java.time d'une manière moins douloureuse. Il s'agit d'un ensemble croissant de recettes que nous continuerons à développer afin d'ajouter plus de couverture avec plus de recettes.
Voici un exemple de migration qui pourrait vous aider à voir comment Sensei facilite la migration du code existant.
De la migration manuelle répétitive à la transformation automatisée du code
Regardons un exemple de création d'une nouvelle DateTime qui démontre quelques pièges cachés lors de la migration d'une simple ligne de code de Joda-Time vers java.time. Nous examinerons ensuite l'une des recettes Sensei de notre livre de recettes Standardization on java.time (JSR-310) et montrerons comment elle capture toutes ces informations, de sorte que cette même migration puisse être réutilisée à l'infini par n'importe quel développeur.
Dans cet exemple, nous construisons un Joda-Time DateTime à partir de 7 arguments int représentant les valeurs des champs DateTime.
Comment migrer vers un équivalent java.time ?
La javadoc de Joda-Time pour ce constructeur dit :
Construit une instance à partir des valeurs du champ date en utilisant ISOChronology dans le fuseau horaire par défaut.
Au départ, on pourrait penser qu'il existe une classe DateTime dans java.time, mais ce n'est pas le cas. Si vous tapez sur Google "migrate from joda time to java time", vous trouverez très probablement le billet de Stephen Colebourne intitulé Converting from Joda-Time to java.time.
Cela vous donne un bon début, et nous oriente vers l'utilisation de java.time.ZonedDateTime ou de java.time.OffsetDateTime. Voici notre première question : lequel dois-je utiliser ? Probablement ZonedDateTime d'après les commentaires de Stephen.
En consultant la javadoc de ZonedDateTime, nous ne voyons aucun constructeur. En revenant à l'article de Stephen, nous lisons plus loin :
Construction. Joda-Time a un constructeur qui accepte un Objet et effectue la conversion de type. java.time n'a que des méthodes de fabrique, donc la conversion est un problème d'utilisateur, bien qu'une méthode parse() soit fournie pour les chaînes de caractères.
Il doit donc exister une méthode d'usine statique. En cherchant dans les méthodes statiques, nous en trouvons une qui semble assez proche, mais qui n'est pas exactement la même.
Il a 7 paramètres int comme notre constructeur DateTime original de Joda-Time, cependant si vous ne faites pas attention vous manquerez un détail important. Le 7ème paramètre ne représente plus des millisecondes, mais des nanosecondes, car java.time a gagné en précision par rapport à Joda-Time et mesure les instants à la nano-seconde. Un détail important que vous auriez pu facilement manquer. En outre, cette méthode attend un ZoneId, ce qui vous amène à vous demander pourquoi vous n'en aviez pas besoin auparavant et pourquoi vous en avez besoin maintenant.
En se rappelant la javadoc de notre constructeur original qui mentionnait qu'il utiliserait le fuseau horaire par défaut, peut-être existe-t-il un moyen d'obtenir le ZoneId par défaut ?
La javadoc de ZoneId ne mentionne aucun constructeur, mais en regardant les méthodes statiques, nous voyons que nous pouvons utiliser systemDefault()
Maintenant que nous avons résolu le problème du ZoneId, que devons-nous faire pour convertir les millisecondes en nanoSecondes ? Peut-être pouvons-nous utiliser java.util.concurrent.TimeUnit pour effectuer la conversion.
Cette méthode renvoie un long, et notre méthode attend un int, donc nous avons maintenant un problème de conversion à résoudre. Nous pourrions peut-être essayer quelque chose de simple. Une multiplication ?
Cela fonctionnera, mais ne semble pas tout à fait à sa place. Si vous ne l'avez pas encore remarqué, nous avons consacré beaucoup de temps et d'efforts à la migration d'une seule ligne de code. Mais comme vous pouvez l'imaginer, nous avons beaucoup de modifications de ce type à faire à la main et cela ne s'améliore pas.
Cependant, si nous nous penchons un peu plus sur l'API java.time, nous pouvons découvrir une solution qui semble un peu plus fluide.
Bien que ZonedDateTime n'ait pas de moyen évident de définir les millisecondes, il est possible de le faire à l'aide de la méthode with(TemporalField field, long newValue), en utilisant ChronoField.MILLI_OF_SECOND comme champ temporel.
Et la documentation Java mentionne qu'elle effectuera la conversion en nanosecondes pour nous :
Lorsque ce champ est utilisé pour définir une valeur, il doit se comporter de la même manière que la valeur NANO_OF_SECOND multipliée par 1 000 000.
Nous pouvons donc simplement spécifier 0 pour nos nanosecondes dans la méthode factory, puis utiliser la méthode with pour créer un ZonedDateTime qui possède toutes les valeurs d'origine ainsi que les millisecondes.
En regardant notre résultat final, on a l'impression que nous n'avons changé qu'une seule ligne de code, ce qui ne montre pas vraiment l'effort qui a été consacré à la recherche d'une seule migration !
Créez une recette pour migrer plus rapidement et plus facilement
Sensei nous permet de partager ces informations durement acquises avec d'autres développeurs. La création d'une recette reprenant toutes ces exigences permettra aux utilisateurs de Sensei d'effectuer cette migration d'un simple clic de souris.
Une recette Sensei se compose de trois parties principales :
- Métadonnées
- Recherche
- Réparations disponibles
Examinons une recette Sensei (qui peut également être considérée comme une recette YAML) qui nous aidera à migrer cet appel vers son équivalent java.time.
DateTime foo = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond) ;
Section des métadonnées
La section des métadonnées contient des informations sur la recette et son utilisation.
Section Recherche
La section de recherche d'une recette Sensei spécifie les éléments de code auxquels cette recette doit s'appliquer.
search :
instanceCreation :
args :
1 :
type : int
2 :
type : int
3 :
type : int
4 :
type : int
5 :
type : int
6 :
type : int
7 :
type : int
argCount : 7
type : org.joda.time.DateTime
Dans cette section de recherche, nous voyons que nous sommes :
- Recherche d'une instanceCreation, c'est-à-dire d'une utilisation d'un Constructeur. Remarque : il existe de nombreuses autres cibles de recherche
- Le constructeur doit avoir 7 arguments, ce qui est spécifié par la propriété argCount
- les arguments 1 à 7 doivent être de type int
- Nous recherchons des constructeurs de type org.joda.time.DateTime
Section des correctifs disponibles
La section availableFixes peut spécifier un ou plusieurs correctifs qui peuvent être appliqués à l'élément de code correspondant. Chaque correctif peut avoir plusieurs actions, et dans notre cas nous avons un seul correctif, qui effectue 2 actions.
- Le nom de la correction est indiqué à l'utilisateur dans le menu "Corrections rapides" et décrit ce qui se passera si l'utilisateur applique cette correction rapide.
- La liste des actions indique les actions qui seront effectuées par cette correction rapide.
- L'action de réécriture réécrit l'élément de code à l'aide d'un modèle Mustache. Elle peut utiliser des variables et des fonctions de remplacement de chaînes.
- L'action modifyAssignedVariable vérifie si ce constructeur est utilisé pour assigner une valeur à une variable. Si c'est le cas, cette action modifiera la variable pour qu'elle soit déclarée comme étant du type spécifié par le type
Utiliser la recette pour transformer le code
Une fois notre recette écrite et activée, il analyse notre code et met en évidence les segments auxquels il peut s'appliquer.
Dans la capture d'écran ci-dessous, nous pouvons voir que le constructeur cible a été marqué par Sensei. En survolant le constructeur marqué, nous voyons la recette shortDescription et l'option Quickfix Migrate to java.time.ZonedDateTime.
Après avoir sélectionné la solution rapide Migrate to java.time.ZonedDateTime, le code est transformé selon les actions que nous avons spécifiées dans la recette.
Une migration unique et des pratiques de codage uniformes dans toutes les équipes - avec Sensei
Nous pouvons voir dans notre exemple ci-dessus que la migration d'une seule ligne de code peut impliquer des connaissances durement acquises. Sensei peut transformer ces connaissances en recettes exploitables ou en livres de cuisine qui peuvent être partagés au sein des équipes. Vous pouvez planifier un sprint de migration unique ou adopter l'approche consistant à effectuer des transformations instantanées incrémentales vers java.time au fur et à mesure que vous rencontrez du code Joda-Time. Vous pouvez activer/désactiver les recettes afin d'effectuer des migrations par étapes logiques et même étendre ou réduire la portée des fichiers analysés par Sensei - la flexibilité qui rend les migrations de code moins pénibles.
La migration des bibliothèques n'est qu'un exemple des nombreuses façons dont Sensei peut être utilisé pour standardiser vos projets. Vous pouvez toujours être à l'affût des anti-modèles ou de certaines transformations de code manuelles que vous rencontrez fréquemment dans les demandes d'extraction ou lorsque vous codez vous-même. Si vous disposez d'un ensemble de directives de codage qui sont souvent ignorées par les développeurs, vous pouvez les convertir en recettes, ce qui permettra aux développeurs d'appliquer les transformations de code approuvées en toute confiance.
Si vous avez des questions, n'hésitez pas à nous contacter ! Rejoignez-nous sur Slack à l'adresse suivante sensei-scw.slack.com
Cameron est développeur de logiciels senior à Secure Code Warrior. Il a plus de 15 ans d'expérience dans la fourniture de logiciels. Il est passionné par la productivité des développeurs et contribue activement aux logiciels libres.
Secure Code Warrior est là pour vous aider à sécuriser le code tout au long du cycle de vie du développement logiciel et à créer une culture dans laquelle la cybersécurité est une priorité. Que vous soyez responsable AppSec, développeur, CISO ou toute autre personne impliquée dans la sécurité, nous pouvons aider votre organisation à réduire les risques associés à un code non sécurisé.
Réservez une démonstrationCameron est développeur de logiciels senior à Secure Code Warrior. Il a plus de 15 ans d'expérience dans la fourniture de logiciels. Il est passionné par la productivité des développeurs et contribue activement aux logiciels libres.
La migration d'un code (lire : d'un code hérité) n'est pas une partie de plaisir. Il faut énormément de planification et d'efforts pour le faire passer. Bien que ce ne soit pas le travail le plus excitant ou le plus motivant pour les développeurs, il faut de la détermination et une bonne expérience pour migrer le code existant vers les nouvelles versions de la bibliothèque. Joda-Time vers java.time est l'une de ces migrations qui nécessite une planification et une exécution méticuleuses.
Si votre projet Java a débuté avant Java SE 8, et qu'il utilise le traitement de la date et de l'heure, il a probablement utilisé Joda-Time - une excellente bibliothèque et un standard de facto pour gérer les fonctions de date et d'heure avant SE 8. Si votre projet utilise toujours Joda-Time mais que vous souhaitez migrer vers java.time, lisez ce qui suit.
La version de Java SE 8 comprenait une nouvelle API standard améliorée pour la date et l'heure, communément appelée java.time (JSR-310). Le projet Joda-Time recommande désormais de migrer vers java.time (JSR-310).
Bien que java.time (JSR-310) ait été fortement inspiré par Joda-Time, il n'est pas rétrocompatible et les concepts et terminologies ont changé. C'est pourquoi la migration de Joda-Time vers java.time nécessite une attention particulière sur chaque ligne de code que vous modifiez. Cela peut prendre beaucoup de temps et vous ferait presque regretter qu'il n'y ait pas un moyen plus facile et automatisé de migrer.
Il existe une meilleure façon de migrer et nous l'avons créée en utilisant Sensei - un plugin IntelliJ pour effectuer automatiquement des transformations de code selon les recettes (règles) que vous avez définies. Passez votre temps à définir des recettes réutilisables, plutôt que d'effectuer des tâches de migration répétitives. L'automatisation ne transformera pas seulement votre code Joda-Time hérité, mais aidera également les équipes à suivre les directives directement dans l'IDE lorsqu'elles écrivent du nouveau code.
Pour vous aider à prendre de l'avance, nous avons créé un livre de recettes public Sensei Standardization on java.time (JSR-310) qui comprend des recettes pour migrer de Joda-Time à java.time d'une manière moins douloureuse. Il s'agit d'un ensemble croissant de recettes que nous continuerons à développer afin d'ajouter plus de couverture avec plus de recettes.
Voici un exemple de migration qui pourrait vous aider à voir comment Sensei facilite la migration du code existant.
De la migration manuelle répétitive à la transformation automatisée du code
Regardons un exemple de création d'une nouvelle DateTime qui démontre quelques pièges cachés lors de la migration d'une simple ligne de code de Joda-Time vers java.time. Nous examinerons ensuite l'une des recettes Sensei de notre livre de recettes Standardization on java.time (JSR-310) et montrerons comment elle capture toutes ces informations, de sorte que cette même migration puisse être réutilisée à l'infini par n'importe quel développeur.
Dans cet exemple, nous construisons un Joda-Time DateTime à partir de 7 arguments int représentant les valeurs des champs DateTime.
Comment migrer vers un équivalent java.time ?
La javadoc de Joda-Time pour ce constructeur dit :
Construit une instance à partir des valeurs du champ date en utilisant ISOChronology dans le fuseau horaire par défaut.
Au départ, on pourrait penser qu'il existe une classe DateTime dans java.time, mais ce n'est pas le cas. Si vous tapez sur Google "migrate from joda time to java time", vous trouverez très probablement le billet de Stephen Colebourne intitulé Converting from Joda-Time to java.time.
Cela vous donne un bon début, et nous oriente vers l'utilisation de java.time.ZonedDateTime ou de java.time.OffsetDateTime. Voici notre première question : lequel dois-je utiliser ? Probablement ZonedDateTime d'après les commentaires de Stephen.
En consultant la javadoc de ZonedDateTime, nous ne voyons aucun constructeur. En revenant à l'article de Stephen, nous lisons plus loin :
Construction. Joda-Time a un constructeur qui accepte un Objet et effectue la conversion de type. java.time n'a que des méthodes de fabrique, donc la conversion est un problème d'utilisateur, bien qu'une méthode parse() soit fournie pour les chaînes de caractères.
Il doit donc exister une méthode d'usine statique. En cherchant dans les méthodes statiques, nous en trouvons une qui semble assez proche, mais qui n'est pas exactement la même.
Il a 7 paramètres int comme notre constructeur DateTime original de Joda-Time, cependant si vous ne faites pas attention vous manquerez un détail important. Le 7ème paramètre ne représente plus des millisecondes, mais des nanosecondes, car java.time a gagné en précision par rapport à Joda-Time et mesure les instants à la nano-seconde. Un détail important que vous auriez pu facilement manquer. En outre, cette méthode attend un ZoneId, ce qui vous amène à vous demander pourquoi vous n'en aviez pas besoin auparavant et pourquoi vous en avez besoin maintenant.
En se rappelant la javadoc de notre constructeur original qui mentionnait qu'il utiliserait le fuseau horaire par défaut, peut-être existe-t-il un moyen d'obtenir le ZoneId par défaut ?
La javadoc de ZoneId ne mentionne aucun constructeur, mais en regardant les méthodes statiques, nous voyons que nous pouvons utiliser systemDefault()
Maintenant que nous avons résolu le problème du ZoneId, que devons-nous faire pour convertir les millisecondes en nanoSecondes ? Peut-être pouvons-nous utiliser java.util.concurrent.TimeUnit pour effectuer la conversion.
Cette méthode renvoie un long, et notre méthode attend un int, donc nous avons maintenant un problème de conversion à résoudre. Nous pourrions peut-être essayer quelque chose de simple. Une multiplication ?
Cela fonctionnera, mais ne semble pas tout à fait à sa place. Si vous ne l'avez pas encore remarqué, nous avons consacré beaucoup de temps et d'efforts à la migration d'une seule ligne de code. Mais comme vous pouvez l'imaginer, nous avons beaucoup de modifications de ce type à faire à la main et cela ne s'améliore pas.
Cependant, si nous nous penchons un peu plus sur l'API java.time, nous pouvons découvrir une solution qui semble un peu plus fluide.
Bien que ZonedDateTime n'ait pas de moyen évident de définir les millisecondes, il est possible de le faire à l'aide de la méthode with(TemporalField field, long newValue), en utilisant ChronoField.MILLI_OF_SECOND comme champ temporel.
Et la documentation Java mentionne qu'elle effectuera la conversion en nanosecondes pour nous :
Lorsque ce champ est utilisé pour définir une valeur, il doit se comporter de la même manière que la valeur NANO_OF_SECOND multipliée par 1 000 000.
Nous pouvons donc simplement spécifier 0 pour nos nanosecondes dans la méthode factory, puis utiliser la méthode with pour créer un ZonedDateTime qui possède toutes les valeurs d'origine ainsi que les millisecondes.
En regardant notre résultat final, on a l'impression que nous n'avons changé qu'une seule ligne de code, ce qui ne montre pas vraiment l'effort qui a été consacré à la recherche d'une seule migration !
Créez une recette pour migrer plus rapidement et plus facilement
Sensei nous permet de partager ces informations durement acquises avec d'autres développeurs. La création d'une recette reprenant toutes ces exigences permettra aux utilisateurs de Sensei d'effectuer cette migration d'un simple clic de souris.
Une recette Sensei se compose de trois parties principales :
- Métadonnées
- Recherche
- Réparations disponibles
Examinons une recette Sensei (qui peut également être considérée comme une recette YAML) qui nous aidera à migrer cet appel vers son équivalent java.time.
DateTime foo = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond) ;
Section des métadonnées
La section des métadonnées contient des informations sur la recette et son utilisation.
Section Recherche
La section de recherche d'une recette Sensei spécifie les éléments de code auxquels cette recette doit s'appliquer.
search :
instanceCreation :
args :
1 :
type : int
2 :
type : int
3 :
type : int
4 :
type : int
5 :
type : int
6 :
type : int
7 :
type : int
argCount : 7
type : org.joda.time.DateTime
Dans cette section de recherche, nous voyons que nous sommes :
- Recherche d'une instanceCreation, c'est-à-dire d'une utilisation d'un Constructeur. Remarque : il existe de nombreuses autres cibles de recherche
- Le constructeur doit avoir 7 arguments, ce qui est spécifié par la propriété argCount
- les arguments 1 à 7 doivent être de type int
- Nous recherchons des constructeurs de type org.joda.time.DateTime
Section des correctifs disponibles
La section availableFixes peut spécifier un ou plusieurs correctifs qui peuvent être appliqués à l'élément de code correspondant. Chaque correctif peut avoir plusieurs actions, et dans notre cas nous avons un seul correctif, qui effectue 2 actions.
- Le nom de la correction est indiqué à l'utilisateur dans le menu "Corrections rapides" et décrit ce qui se passera si l'utilisateur applique cette correction rapide.
- La liste des actions indique les actions qui seront effectuées par cette correction rapide.
- L'action de réécriture réécrit l'élément de code à l'aide d'un modèle Mustache. Elle peut utiliser des variables et des fonctions de remplacement de chaînes.
- L'action modifyAssignedVariable vérifie si ce constructeur est utilisé pour assigner une valeur à une variable. Si c'est le cas, cette action modifiera la variable pour qu'elle soit déclarée comme étant du type spécifié par le type
Utiliser la recette pour transformer le code
Une fois notre recette écrite et activée, il analyse notre code et met en évidence les segments auxquels il peut s'appliquer.
Dans la capture d'écran ci-dessous, nous pouvons voir que le constructeur cible a été marqué par Sensei. En survolant le constructeur marqué, nous voyons la recette shortDescription et l'option Quickfix Migrate to java.time.ZonedDateTime.
Après avoir sélectionné la solution rapide Migrate to java.time.ZonedDateTime, le code est transformé selon les actions que nous avons spécifiées dans la recette.
Une migration unique et des pratiques de codage uniformes dans toutes les équipes - avec Sensei
Nous pouvons voir dans notre exemple ci-dessus que la migration d'une seule ligne de code peut impliquer des connaissances durement acquises. Sensei peut transformer ces connaissances en recettes exploitables ou en livres de cuisine qui peuvent être partagés au sein des équipes. Vous pouvez planifier un sprint de migration unique ou adopter l'approche consistant à effectuer des transformations instantanées incrémentales vers java.time au fur et à mesure que vous rencontrez du code Joda-Time. Vous pouvez activer/désactiver les recettes afin d'effectuer des migrations par étapes logiques et même étendre ou réduire la portée des fichiers analysés par Sensei - la flexibilité qui rend les migrations de code moins pénibles.
La migration des bibliothèques n'est qu'un exemple des nombreuses façons dont Sensei peut être utilisé pour standardiser vos projets. Vous pouvez toujours être à l'affût des anti-modèles ou de certaines transformations de code manuelles que vous rencontrez fréquemment dans les demandes d'extraction ou lorsque vous codez vous-même. Si vous disposez d'un ensemble de directives de codage qui sont souvent ignorées par les développeurs, vous pouvez les convertir en recettes, ce qui permettra aux développeurs d'appliquer les transformations de code approuvées en toute confiance.
Si vous avez des questions, n'hésitez pas à nous contacter ! Rejoignez-nous sur Slack à l'adresse suivante sensei-scw.slack.com
La migration d'un code (lire : d'un code hérité) n'est pas une partie de plaisir. Il faut énormément de planification et d'efforts pour le faire passer. Bien que ce ne soit pas le travail le plus excitant ou le plus motivant pour les développeurs, il faut de la détermination et une bonne expérience pour migrer le code existant vers les nouvelles versions de la bibliothèque. Joda-Time vers java.time est l'une de ces migrations qui nécessite une planification et une exécution méticuleuses.
Si votre projet Java a débuté avant Java SE 8, et qu'il utilise le traitement de la date et de l'heure, il a probablement utilisé Joda-Time - une excellente bibliothèque et un standard de facto pour gérer les fonctions de date et d'heure avant SE 8. Si votre projet utilise toujours Joda-Time mais que vous souhaitez migrer vers java.time, lisez ce qui suit.
La version de Java SE 8 comprenait une nouvelle API standard améliorée pour la date et l'heure, communément appelée java.time (JSR-310). Le projet Joda-Time recommande désormais de migrer vers java.time (JSR-310).
Bien que java.time (JSR-310) ait été fortement inspiré par Joda-Time, il n'est pas rétrocompatible et les concepts et terminologies ont changé. C'est pourquoi la migration de Joda-Time vers java.time nécessite une attention particulière sur chaque ligne de code que vous modifiez. Cela peut prendre beaucoup de temps et vous ferait presque regretter qu'il n'y ait pas un moyen plus facile et automatisé de migrer.
Il existe une meilleure façon de migrer et nous l'avons créée en utilisant Sensei - un plugin IntelliJ pour effectuer automatiquement des transformations de code selon les recettes (règles) que vous avez définies. Passez votre temps à définir des recettes réutilisables, plutôt que d'effectuer des tâches de migration répétitives. L'automatisation ne transformera pas seulement votre code Joda-Time hérité, mais aidera également les équipes à suivre les directives directement dans l'IDE lorsqu'elles écrivent du nouveau code.
Pour vous aider à prendre de l'avance, nous avons créé un livre de recettes public Sensei Standardization on java.time (JSR-310) qui comprend des recettes pour migrer de Joda-Time à java.time d'une manière moins douloureuse. Il s'agit d'un ensemble croissant de recettes que nous continuerons à développer afin d'ajouter plus de couverture avec plus de recettes.
Voici un exemple de migration qui pourrait vous aider à voir comment Sensei facilite la migration du code existant.
De la migration manuelle répétitive à la transformation automatisée du code
Regardons un exemple de création d'une nouvelle DateTime qui démontre quelques pièges cachés lors de la migration d'une simple ligne de code de Joda-Time vers java.time. Nous examinerons ensuite l'une des recettes Sensei de notre livre de recettes Standardization on java.time (JSR-310) et montrerons comment elle capture toutes ces informations, de sorte que cette même migration puisse être réutilisée à l'infini par n'importe quel développeur.
Dans cet exemple, nous construisons un Joda-Time DateTime à partir de 7 arguments int représentant les valeurs des champs DateTime.
Comment migrer vers un équivalent java.time ?
La javadoc de Joda-Time pour ce constructeur dit :
Construit une instance à partir des valeurs du champ date en utilisant ISOChronology dans le fuseau horaire par défaut.
Au départ, on pourrait penser qu'il existe une classe DateTime dans java.time, mais ce n'est pas le cas. Si vous tapez sur Google "migrate from joda time to java time", vous trouverez très probablement le billet de Stephen Colebourne intitulé Converting from Joda-Time to java.time.
Cela vous donne un bon début, et nous oriente vers l'utilisation de java.time.ZonedDateTime ou de java.time.OffsetDateTime. Voici notre première question : lequel dois-je utiliser ? Probablement ZonedDateTime d'après les commentaires de Stephen.
En consultant la javadoc de ZonedDateTime, nous ne voyons aucun constructeur. En revenant à l'article de Stephen, nous lisons plus loin :
Construction. Joda-Time a un constructeur qui accepte un Objet et effectue la conversion de type. java.time n'a que des méthodes de fabrique, donc la conversion est un problème d'utilisateur, bien qu'une méthode parse() soit fournie pour les chaînes de caractères.
Il doit donc exister une méthode d'usine statique. En cherchant dans les méthodes statiques, nous en trouvons une qui semble assez proche, mais qui n'est pas exactement la même.
Il a 7 paramètres int comme notre constructeur DateTime original de Joda-Time, cependant si vous ne faites pas attention vous manquerez un détail important. Le 7ème paramètre ne représente plus des millisecondes, mais des nanosecondes, car java.time a gagné en précision par rapport à Joda-Time et mesure les instants à la nano-seconde. Un détail important que vous auriez pu facilement manquer. En outre, cette méthode attend un ZoneId, ce qui vous amène à vous demander pourquoi vous n'en aviez pas besoin auparavant et pourquoi vous en avez besoin maintenant.
En se rappelant la javadoc de notre constructeur original qui mentionnait qu'il utiliserait le fuseau horaire par défaut, peut-être existe-t-il un moyen d'obtenir le ZoneId par défaut ?
La javadoc de ZoneId ne mentionne aucun constructeur, mais en regardant les méthodes statiques, nous voyons que nous pouvons utiliser systemDefault()
Maintenant que nous avons résolu le problème du ZoneId, que devons-nous faire pour convertir les millisecondes en nanoSecondes ? Peut-être pouvons-nous utiliser java.util.concurrent.TimeUnit pour effectuer la conversion.
Cette méthode renvoie un long, et notre méthode attend un int, donc nous avons maintenant un problème de conversion à résoudre. Nous pourrions peut-être essayer quelque chose de simple. Une multiplication ?
Cela fonctionnera, mais ne semble pas tout à fait à sa place. Si vous ne l'avez pas encore remarqué, nous avons consacré beaucoup de temps et d'efforts à la migration d'une seule ligne de code. Mais comme vous pouvez l'imaginer, nous avons beaucoup de modifications de ce type à faire à la main et cela ne s'améliore pas.
Cependant, si nous nous penchons un peu plus sur l'API java.time, nous pouvons découvrir une solution qui semble un peu plus fluide.
Bien que ZonedDateTime n'ait pas de moyen évident de définir les millisecondes, il est possible de le faire à l'aide de la méthode with(TemporalField field, long newValue), en utilisant ChronoField.MILLI_OF_SECOND comme champ temporel.
Et la documentation Java mentionne qu'elle effectuera la conversion en nanosecondes pour nous :
Lorsque ce champ est utilisé pour définir une valeur, il doit se comporter de la même manière que la valeur NANO_OF_SECOND multipliée par 1 000 000.
Nous pouvons donc simplement spécifier 0 pour nos nanosecondes dans la méthode factory, puis utiliser la méthode with pour créer un ZonedDateTime qui possède toutes les valeurs d'origine ainsi que les millisecondes.
En regardant notre résultat final, on a l'impression que nous n'avons changé qu'une seule ligne de code, ce qui ne montre pas vraiment l'effort qui a été consacré à la recherche d'une seule migration !
Créez une recette pour migrer plus rapidement et plus facilement
Sensei nous permet de partager ces informations durement acquises avec d'autres développeurs. La création d'une recette reprenant toutes ces exigences permettra aux utilisateurs de Sensei d'effectuer cette migration d'un simple clic de souris.
Une recette Sensei se compose de trois parties principales :
- Métadonnées
- Recherche
- Réparations disponibles
Examinons une recette Sensei (qui peut également être considérée comme une recette YAML) qui nous aidera à migrer cet appel vers son équivalent java.time.
DateTime foo = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond) ;
Section des métadonnées
La section des métadonnées contient des informations sur la recette et son utilisation.
Section Recherche
La section de recherche d'une recette Sensei spécifie les éléments de code auxquels cette recette doit s'appliquer.
search :
instanceCreation :
args :
1 :
type : int
2 :
type : int
3 :
type : int
4 :
type : int
5 :
type : int
6 :
type : int
7 :
type : int
argCount : 7
type : org.joda.time.DateTime
Dans cette section de recherche, nous voyons que nous sommes :
- Recherche d'une instanceCreation, c'est-à-dire d'une utilisation d'un Constructeur. Remarque : il existe de nombreuses autres cibles de recherche
- Le constructeur doit avoir 7 arguments, ce qui est spécifié par la propriété argCount
- les arguments 1 à 7 doivent être de type int
- Nous recherchons des constructeurs de type org.joda.time.DateTime
Section des correctifs disponibles
La section availableFixes peut spécifier un ou plusieurs correctifs qui peuvent être appliqués à l'élément de code correspondant. Chaque correctif peut avoir plusieurs actions, et dans notre cas nous avons un seul correctif, qui effectue 2 actions.
- Le nom de la correction est indiqué à l'utilisateur dans le menu "Corrections rapides" et décrit ce qui se passera si l'utilisateur applique cette correction rapide.
- La liste des actions indique les actions qui seront effectuées par cette correction rapide.
- L'action de réécriture réécrit l'élément de code à l'aide d'un modèle Mustache. Elle peut utiliser des variables et des fonctions de remplacement de chaînes.
- L'action modifyAssignedVariable vérifie si ce constructeur est utilisé pour assigner une valeur à une variable. Si c'est le cas, cette action modifiera la variable pour qu'elle soit déclarée comme étant du type spécifié par le type
Utiliser la recette pour transformer le code
Une fois notre recette écrite et activée, il analyse notre code et met en évidence les segments auxquels il peut s'appliquer.
Dans la capture d'écran ci-dessous, nous pouvons voir que le constructeur cible a été marqué par Sensei. En survolant le constructeur marqué, nous voyons la recette shortDescription et l'option Quickfix Migrate to java.time.ZonedDateTime.
Après avoir sélectionné la solution rapide Migrate to java.time.ZonedDateTime, le code est transformé selon les actions que nous avons spécifiées dans la recette.
Une migration unique et des pratiques de codage uniformes dans toutes les équipes - avec Sensei
Nous pouvons voir dans notre exemple ci-dessus que la migration d'une seule ligne de code peut impliquer des connaissances durement acquises. Sensei peut transformer ces connaissances en recettes exploitables ou en livres de cuisine qui peuvent être partagés au sein des équipes. Vous pouvez planifier un sprint de migration unique ou adopter l'approche consistant à effectuer des transformations instantanées incrémentales vers java.time au fur et à mesure que vous rencontrez du code Joda-Time. Vous pouvez activer/désactiver les recettes afin d'effectuer des migrations par étapes logiques et même étendre ou réduire la portée des fichiers analysés par Sensei - la flexibilité qui rend les migrations de code moins pénibles.
La migration des bibliothèques n'est qu'un exemple des nombreuses façons dont Sensei peut être utilisé pour standardiser vos projets. Vous pouvez toujours être à l'affût des anti-modèles ou de certaines transformations de code manuelles que vous rencontrez fréquemment dans les demandes d'extraction ou lorsque vous codez vous-même. Si vous disposez d'un ensemble de directives de codage qui sont souvent ignorées par les développeurs, vous pouvez les convertir en recettes, ce qui permettra aux développeurs d'appliquer les transformations de code approuvées en toute confiance.
Si vous avez des questions, n'hésitez pas à nous contacter ! Rejoignez-nous sur Slack à l'adresse suivante sensei-scw.slack.com
Cliquez sur le lien ci-dessous et téléchargez le PDF de cette ressource.
Secure Code Warrior est là pour vous aider à sécuriser le code tout au long du cycle de vie du développement logiciel et à créer une culture dans laquelle la cybersécurité est une priorité. Que vous soyez responsable AppSec, développeur, CISO ou toute autre personne impliquée dans la sécurité, nous pouvons aider votre organisation à réduire les risques associés à un code non sécurisé.
Voir le rapportRéservez une démonstrationCameron est développeur de logiciels senior à Secure Code Warrior. Il a plus de 15 ans d'expérience dans la fourniture de logiciels. Il est passionné par la productivité des développeurs et contribue activement aux logiciels libres.
La migration d'un code (lire : d'un code hérité) n'est pas une partie de plaisir. Il faut énormément de planification et d'efforts pour le faire passer. Bien que ce ne soit pas le travail le plus excitant ou le plus motivant pour les développeurs, il faut de la détermination et une bonne expérience pour migrer le code existant vers les nouvelles versions de la bibliothèque. Joda-Time vers java.time est l'une de ces migrations qui nécessite une planification et une exécution méticuleuses.
Si votre projet Java a débuté avant Java SE 8, et qu'il utilise le traitement de la date et de l'heure, il a probablement utilisé Joda-Time - une excellente bibliothèque et un standard de facto pour gérer les fonctions de date et d'heure avant SE 8. Si votre projet utilise toujours Joda-Time mais que vous souhaitez migrer vers java.time, lisez ce qui suit.
La version de Java SE 8 comprenait une nouvelle API standard améliorée pour la date et l'heure, communément appelée java.time (JSR-310). Le projet Joda-Time recommande désormais de migrer vers java.time (JSR-310).
Bien que java.time (JSR-310) ait été fortement inspiré par Joda-Time, il n'est pas rétrocompatible et les concepts et terminologies ont changé. C'est pourquoi la migration de Joda-Time vers java.time nécessite une attention particulière sur chaque ligne de code que vous modifiez. Cela peut prendre beaucoup de temps et vous ferait presque regretter qu'il n'y ait pas un moyen plus facile et automatisé de migrer.
Il existe une meilleure façon de migrer et nous l'avons créée en utilisant Sensei - un plugin IntelliJ pour effectuer automatiquement des transformations de code selon les recettes (règles) que vous avez définies. Passez votre temps à définir des recettes réutilisables, plutôt que d'effectuer des tâches de migration répétitives. L'automatisation ne transformera pas seulement votre code Joda-Time hérité, mais aidera également les équipes à suivre les directives directement dans l'IDE lorsqu'elles écrivent du nouveau code.
Pour vous aider à prendre de l'avance, nous avons créé un livre de recettes public Sensei Standardization on java.time (JSR-310) qui comprend des recettes pour migrer de Joda-Time à java.time d'une manière moins douloureuse. Il s'agit d'un ensemble croissant de recettes que nous continuerons à développer afin d'ajouter plus de couverture avec plus de recettes.
Voici un exemple de migration qui pourrait vous aider à voir comment Sensei facilite la migration du code existant.
De la migration manuelle répétitive à la transformation automatisée du code
Regardons un exemple de création d'une nouvelle DateTime qui démontre quelques pièges cachés lors de la migration d'une simple ligne de code de Joda-Time vers java.time. Nous examinerons ensuite l'une des recettes Sensei de notre livre de recettes Standardization on java.time (JSR-310) et montrerons comment elle capture toutes ces informations, de sorte que cette même migration puisse être réutilisée à l'infini par n'importe quel développeur.
Dans cet exemple, nous construisons un Joda-Time DateTime à partir de 7 arguments int représentant les valeurs des champs DateTime.
Comment migrer vers un équivalent java.time ?
La javadoc de Joda-Time pour ce constructeur dit :
Construit une instance à partir des valeurs du champ date en utilisant ISOChronology dans le fuseau horaire par défaut.
Au départ, on pourrait penser qu'il existe une classe DateTime dans java.time, mais ce n'est pas le cas. Si vous tapez sur Google "migrate from joda time to java time", vous trouverez très probablement le billet de Stephen Colebourne intitulé Converting from Joda-Time to java.time.
Cela vous donne un bon début, et nous oriente vers l'utilisation de java.time.ZonedDateTime ou de java.time.OffsetDateTime. Voici notre première question : lequel dois-je utiliser ? Probablement ZonedDateTime d'après les commentaires de Stephen.
En consultant la javadoc de ZonedDateTime, nous ne voyons aucun constructeur. En revenant à l'article de Stephen, nous lisons plus loin :
Construction. Joda-Time a un constructeur qui accepte un Objet et effectue la conversion de type. java.time n'a que des méthodes de fabrique, donc la conversion est un problème d'utilisateur, bien qu'une méthode parse() soit fournie pour les chaînes de caractères.
Il doit donc exister une méthode d'usine statique. En cherchant dans les méthodes statiques, nous en trouvons une qui semble assez proche, mais qui n'est pas exactement la même.
Il a 7 paramètres int comme notre constructeur DateTime original de Joda-Time, cependant si vous ne faites pas attention vous manquerez un détail important. Le 7ème paramètre ne représente plus des millisecondes, mais des nanosecondes, car java.time a gagné en précision par rapport à Joda-Time et mesure les instants à la nano-seconde. Un détail important que vous auriez pu facilement manquer. En outre, cette méthode attend un ZoneId, ce qui vous amène à vous demander pourquoi vous n'en aviez pas besoin auparavant et pourquoi vous en avez besoin maintenant.
En se rappelant la javadoc de notre constructeur original qui mentionnait qu'il utiliserait le fuseau horaire par défaut, peut-être existe-t-il un moyen d'obtenir le ZoneId par défaut ?
La javadoc de ZoneId ne mentionne aucun constructeur, mais en regardant les méthodes statiques, nous voyons que nous pouvons utiliser systemDefault()
Maintenant que nous avons résolu le problème du ZoneId, que devons-nous faire pour convertir les millisecondes en nanoSecondes ? Peut-être pouvons-nous utiliser java.util.concurrent.TimeUnit pour effectuer la conversion.
Cette méthode renvoie un long, et notre méthode attend un int, donc nous avons maintenant un problème de conversion à résoudre. Nous pourrions peut-être essayer quelque chose de simple. Une multiplication ?
Cela fonctionnera, mais ne semble pas tout à fait à sa place. Si vous ne l'avez pas encore remarqué, nous avons consacré beaucoup de temps et d'efforts à la migration d'une seule ligne de code. Mais comme vous pouvez l'imaginer, nous avons beaucoup de modifications de ce type à faire à la main et cela ne s'améliore pas.
Cependant, si nous nous penchons un peu plus sur l'API java.time, nous pouvons découvrir une solution qui semble un peu plus fluide.
Bien que ZonedDateTime n'ait pas de moyen évident de définir les millisecondes, il est possible de le faire à l'aide de la méthode with(TemporalField field, long newValue), en utilisant ChronoField.MILLI_OF_SECOND comme champ temporel.
Et la documentation Java mentionne qu'elle effectuera la conversion en nanosecondes pour nous :
Lorsque ce champ est utilisé pour définir une valeur, il doit se comporter de la même manière que la valeur NANO_OF_SECOND multipliée par 1 000 000.
Nous pouvons donc simplement spécifier 0 pour nos nanosecondes dans la méthode factory, puis utiliser la méthode with pour créer un ZonedDateTime qui possède toutes les valeurs d'origine ainsi que les millisecondes.
En regardant notre résultat final, on a l'impression que nous n'avons changé qu'une seule ligne de code, ce qui ne montre pas vraiment l'effort qui a été consacré à la recherche d'une seule migration !
Créez une recette pour migrer plus rapidement et plus facilement
Sensei nous permet de partager ces informations durement acquises avec d'autres développeurs. La création d'une recette reprenant toutes ces exigences permettra aux utilisateurs de Sensei d'effectuer cette migration d'un simple clic de souris.
Une recette Sensei se compose de trois parties principales :
- Métadonnées
- Recherche
- Réparations disponibles
Examinons une recette Sensei (qui peut également être considérée comme une recette YAML) qui nous aidera à migrer cet appel vers son équivalent java.time.
DateTime foo = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond) ;
Section des métadonnées
La section des métadonnées contient des informations sur la recette et son utilisation.
Section Recherche
La section de recherche d'une recette Sensei spécifie les éléments de code auxquels cette recette doit s'appliquer.
search :
instanceCreation :
args :
1 :
type : int
2 :
type : int
3 :
type : int
4 :
type : int
5 :
type : int
6 :
type : int
7 :
type : int
argCount : 7
type : org.joda.time.DateTime
Dans cette section de recherche, nous voyons que nous sommes :
- Recherche d'une instanceCreation, c'est-à-dire d'une utilisation d'un Constructeur. Remarque : il existe de nombreuses autres cibles de recherche
- Le constructeur doit avoir 7 arguments, ce qui est spécifié par la propriété argCount
- les arguments 1 à 7 doivent être de type int
- Nous recherchons des constructeurs de type org.joda.time.DateTime
Section des correctifs disponibles
La section availableFixes peut spécifier un ou plusieurs correctifs qui peuvent être appliqués à l'élément de code correspondant. Chaque correctif peut avoir plusieurs actions, et dans notre cas nous avons un seul correctif, qui effectue 2 actions.
- Le nom de la correction est indiqué à l'utilisateur dans le menu "Corrections rapides" et décrit ce qui se passera si l'utilisateur applique cette correction rapide.
- La liste des actions indique les actions qui seront effectuées par cette correction rapide.
- L'action de réécriture réécrit l'élément de code à l'aide d'un modèle Mustache. Elle peut utiliser des variables et des fonctions de remplacement de chaînes.
- L'action modifyAssignedVariable vérifie si ce constructeur est utilisé pour assigner une valeur à une variable. Si c'est le cas, cette action modifiera la variable pour qu'elle soit déclarée comme étant du type spécifié par le type
Utiliser la recette pour transformer le code
Une fois notre recette écrite et activée, il analyse notre code et met en évidence les segments auxquels il peut s'appliquer.
Dans la capture d'écran ci-dessous, nous pouvons voir que le constructeur cible a été marqué par Sensei. En survolant le constructeur marqué, nous voyons la recette shortDescription et l'option Quickfix Migrate to java.time.ZonedDateTime.
Après avoir sélectionné la solution rapide Migrate to java.time.ZonedDateTime, le code est transformé selon les actions que nous avons spécifiées dans la recette.
Une migration unique et des pratiques de codage uniformes dans toutes les équipes - avec Sensei
Nous pouvons voir dans notre exemple ci-dessus que la migration d'une seule ligne de code peut impliquer des connaissances durement acquises. Sensei peut transformer ces connaissances en recettes exploitables ou en livres de cuisine qui peuvent être partagés au sein des équipes. Vous pouvez planifier un sprint de migration unique ou adopter l'approche consistant à effectuer des transformations instantanées incrémentales vers java.time au fur et à mesure que vous rencontrez du code Joda-Time. Vous pouvez activer/désactiver les recettes afin d'effectuer des migrations par étapes logiques et même étendre ou réduire la portée des fichiers analysés par Sensei - la flexibilité qui rend les migrations de code moins pénibles.
La migration des bibliothèques n'est qu'un exemple des nombreuses façons dont Sensei peut être utilisé pour standardiser vos projets. Vous pouvez toujours être à l'affût des anti-modèles ou de certaines transformations de code manuelles que vous rencontrez fréquemment dans les demandes d'extraction ou lorsque vous codez vous-même. Si vous disposez d'un ensemble de directives de codage qui sont souvent ignorées par les développeurs, vous pouvez les convertir en recettes, ce qui permettra aux développeurs d'appliquer les transformations de code approuvées en toute confiance.
Si vous avez des questions, n'hésitez pas à nous contacter ! Rejoignez-nous sur Slack à l'adresse suivante sensei-scw.slack.com
Table des matières
Cameron est développeur de logiciels senior à Secure Code Warrior. Il a plus de 15 ans d'expérience dans la fourniture de logiciels. Il est passionné par la productivité des développeurs et contribue activement aux logiciels libres.
Secure Code Warrior est là pour vous aider à sécuriser le code tout au long du cycle de vie du développement logiciel et à créer une culture dans laquelle la cybersécurité est une priorité. Que vous soyez responsable AppSec, développeur, CISO ou toute autre personne impliquée dans la sécurité, nous pouvons aider votre organisation à réduire les risques associés à un code non sécurisé.
Réservez une démonstrationTéléchargerRessources pour vous aider à démarrer
Évaluation comparative des compétences en matière de sécurité : Rationalisation de la conception sécurisée dans l'entreprise
Le mouvement "Secure-by-Design" (conception sécurisée) est l'avenir du développement de logiciels sécurisés. Découvrez les éléments clés que les entreprises doivent garder à l'esprit lorsqu'elles envisagent une initiative de conception sécurisée.
DigitalOcean réduit sa dette de sécurité avec Secure Code Warrior
L'utilisation par DigitalOcean de la formation Secure Code Warrior a considérablement réduit la dette de sécurité, permettant aux équipes de se concentrer davantage sur l'innovation et la productivité. L'amélioration de la sécurité a renforcé la qualité des produits et l'avantage concurrentiel de l'entreprise. À l'avenir, le score de confiance SCW les aidera à améliorer leurs pratiques de sécurité et à continuer à stimuler l'innovation.
Ressources pour vous aider à démarrer
Sécurité réactive contre sécurité préventive : La prévention est un meilleur remède
L'idée d'apporter une sécurité préventive aux codes et systèmes existants en même temps qu'aux applications plus récentes peut sembler décourageante, mais une approche "Secure-by-Design", mise en œuvre en améliorant les compétences des développeurs, permet d'appliquer les meilleures pratiques de sécurité à ces systèmes. C'est la meilleure chance qu'ont de nombreuses organisations d'améliorer leur sécurité.
Les avantages de l'évaluation des compétences des développeurs en matière de sécurité
L'importance croissante accordée au code sécurisé et aux principes de conception sécurisée exige que les développeurs soient formés à la cybersécurité dès le début du cycle de développement durable, et que des outils tels que le Trust Score de Secure Code Warriorles aident à mesurer et à améliorer leurs progrès.
Assurer le succès des initiatives de conception sécurisée de l'entreprise
Notre dernier document de recherche, Benchmarking Security Skills : Streamlining Secure-by-Design in the Enterprise est le résultat d'une analyse approfondie d'initiatives réelles de conception sécurisée au niveau de l'entreprise, et de l'élaboration d'approches de meilleures pratiques basées sur des conclusions fondées sur des données.
Plongée en profondeur : Naviguer dans la vulnérabilité critique de CUPS dans les systèmes GNU-Linux
Découvrez les derniers défis de sécurité auxquels sont confrontés les utilisateurs de Linux en explorant les récentes vulnérabilités de haute sévérité dans le système d'impression commun d'UNIX (CUPS). Apprenez comment ces problèmes peuvent conduire à une potentielle exécution de code à distance (RCE) et ce que vous pouvez faire pour protéger vos systèmes.