IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Java Discussion :

Identifier les IncompatibleClassChangeError potentielles à partir du bytecode


Sujet :

Langage Java

  1. #1
    Membre averti
    Avatar de Chatanga
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    211
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 211
    Points : 346
    Points
    346
    Par défaut Identifier les IncompatibleClassChangeError potentielles à partir du bytecode
    Soit une interface A exposant une méthode f et une classe B implémentant cette interface. On compile et ça marche. On ajoute une nouvelle méthode g à l'interface A. On recompile uniquement A. Le code est toujours fonctionnel tant qu'on ne fait pas appel à la méthode g sur une variable de type A contenant une instance de B. Dans le cas contraire, une IncompatibleClassChangeError est levée.

    La manière habituelle pour détecter ce genre de problème est évidemment de recompiler tous les fichiers Java dépendant de ceux qu'on a modifiés, mais que faire lorsqu'on a que le bytecode (donc les fichiers *.class) sous la main ? Existe-t-il un outil permettant de faire cette vérification "statique" ? Le chargeur de classes du système inclut bien un bytecode verifier, mais il ne vérifie pas ce genre de chose (pas directement du moins ni donc exhaustivement).

  2. #2
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    grosso merdo:

    getClass().getMethode("laNouvelleMethode")

    devrais te dire si la classe implémente bien la nouvelle méthode. Généralement, comme tu le dit si bien, on met à jour l'implémentation après avoir mis à jour l'interface. Concrètement, je ne vois pas pourquoi tu ne modifie pas la classe qui implémente l'interface pour ensuite la recompiler....

    Imaginons, par exemple, que ton interface soit celle à implémenter par des "plugins" de ton application. Tu n'a pas le contrôle des plugins (logique, ils sont tierce partie). En version 2 de ton programme, tu veux rajouter des fonctionnalités au plugin, ils fautdes méthodes supplémentaires sur l'interfaces sans casses les plugins existant. La solution est de dériver une nouvelle interface de l'ancienne, exemple "Plugin2", charger le plugin, et regarder si ca classe est "instaceOf Plugin2", et le tour est joué, pas de IncompatibleClassChangeError

    Code text : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
     
    Interface A    <--- extends --- Interface A2 (nouvelle méthode g() )
        / \                              / \
         |                                |
         |                                |
    Classe X                         Classe Y
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    if (o instanceof A2)
       ((A2)o).g();
    else
       // ancienne interface seulement

  3. #3
    Membre averti
    Avatar de Chatanga
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    211
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 211
    Points : 346
    Points
    346
    Par défaut
    Mon objectif est différent. Disons que mon application est découpée en modules qui produisent autant de fichiers JAR. Il y a bien sûr des dépendances entre ces modules, mais j'aimerais (pour des raisons de performances qui ne se résument pas au seul javac) ne recompiler que les modules ayant changé et, pour les autres qui n'ont pas changé mais en dépendent, simplement vérifier qu'il restent cohérents (p. ex. l'interface A est dans un module et la classe B dans un autre).

    C'est donc un mécanisme générique qui m'intéresse puisque je ne connais pas le détail des classes à vérifier (et l'exemple donné A <- B n'est qu'un cas de IncompatibleClassChangeError parmi d'autres).

  4. #4
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    tous les projet qui dépendent du projet modifié doivent, à priori, être recompiler. Si les perfs te posent problème, il suffit d'opter pour un nighlty build de te projet sur un serveur quelconque et quand t'arrivera le matin, tu aura la release toute propre qui a compilé pendant la nuit.

    Il n'y a pas que les interface, beaucoup de changements dans le projet maître peuvent avoir des implication dans les projets qui en dépendent (changement de version d'une librairie, ....)

  5. #5
    Membre averti
    Avatar de Chatanga
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    211
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 211
    Points : 346
    Points
    346
    Par défaut
    Le nightly build se porte bien, c'est du continuous build (lancé à chaque commit) dont il est question dans mon cas. Qu'importe ces détails toutefois, la question n'est pas là.

    Un module donné qui n'a pas changé lui même n'a pas besoin d'être recompilé car sa recompilation donnera strictement le même résultat que la fois précédente (exception faite des expressions constantes). Je fais abstraction des changements dans les dépendances de production : si le build process lui-même change (ou simplement une version d'outil utilisé pour précompiler), il faut évidemment recompiler. Le seul intérêt de recompiler un module qui n'a pas changé est de vérifier qu'il est toujours compatible avec ses dépendances qui ont changé. Il s'agit en fait juste de réaliser les mêmes tests statiques qu'une compilation sans pour autant recompiler. Pour savoir si le modules fonctionne réellement comme attendu (et pas seulement qu'il peut le faire), il y a les tests (unitaires en particulier).

  6. #6
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    sauf que dans le cas qui nous concerne, le module en question ne donnera pas le même résultat à la recompilation puisque tu a changé une interface et qu'il n'implémente pas la nouvelle méthode! (d'ou l'exception). Si ton problème est de détecter cette erreur, ca devrais se détecter en aval.


    Si je te suis bien.

    Module B depend de module A

    Module A changé (modification d'un interface)
    Module B pas changé

    A recompilé (continuous build)
    B pas recompilé (pas de détection du problème donc)

    mais au final B est bien utilisé quelque part. Soit c'est le produit fini -> erreur sera détecter lors du daily (un peu tard mais détectée), soit c'est un produit intermédiaire. Dans ce dernier cas, lorsque B sera utilisé par le module C, on verra dans l'intégration continue de C qu'il déclenche des erreur dans le module B et on remarquera alors qu'il faut corriger B.

    Dans le cadre d'un intégration continue, je vois de toutes facons pas commende depuis A tu pourrais détecter que B ne marche plus, puisque A ne connais, a priori, pas B, donc même en supposant que tu sois capable de détecter ça point de vue bytecode, je vois pas où t'irais mettre ce test. Pas dans A car il ne vois pas B, pas dans B car il n'est pas recompilé

  7. #7
    Membre averti
    Avatar de Chatanga
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    211
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 211
    Points : 346
    Points
    346
    Par défaut
    Citation Envoyé par tchize_ Voir le message
    sauf que dans le cas qui nous concerne, le module en question ne donnera pas le même résultat à la recompilation puisque tu a changé une interface et qu'il n'implémente pas la nouvelle méthode! (d'ou l'exception).
    Ok, il ne donnera pas le même résultat dans le sens où il ne donnera rien du tout (si ce n'est une erreur). Par contre, il ne produira jamais des .class différents.

    Soyons plus précis :

    Processus de production :
    Pour chaque module m de la liste des modules du projet (classée de manière à ce que m1 -> m2 => index(m2) < index(m1)) :
    a) Si m a changé de lui-même :
    Recompiler m et le republier.
    b) Sinon, si une dépendance (directe ou indirecte) de m a changé :
    Ne pas recompiler m mais simplement vérifier la cohérence de son bytecode vis à vis de ses dépendances.
    c) Sinon :
    Ne rien faire.

    Dans le cas b), de deux choses l'une pour le module m :
    • ou bien il serait recompilé avec succès en produisant strictement la même chose à publier (inutile donc de le recompiler) ;
    • ou bien il y aurait des erreurs de compilation.


    L'idée d'examiner le bytecode est juste de savoir directement s'il existe des erreurs statiques (telles que l'exemple de la méthode g ajoutée) dans les classes qu'on possède déjà de la précédente production, sans passer par la compilation complète (analyse lexicale, syntaxique, etc.) dont on se fiche (je sais que javac est très rapide mais la question n'est pas là).

  8. #8
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    je doit dire que dans ton étape deux, entre charger chaque .class et vérifier dans le bytecode si le module est compatible ou recompiler simplement le module, mon choix serait vite fait. La deuxième option est bien plus rapide à mettre en place et pas plus longue.....

Discussions similaires

  1. Réponses: 0
    Dernier message: 16/07/2013, 18h55
  2. Identifier les enregistrements Lu d'une table
    Par aityahia dans le forum Bases de données
    Réponses: 3
    Dernier message: 25/03/2006, 19h50
  3. Identifier un FAI a partir d'une IP
    Par roger12 dans le forum Dépannage et Assistance
    Réponses: 3
    Dernier message: 10/03/2006, 17h10
  4. Code source à partir du bytecode
    Par marocleverness dans le forum EDI et Outils pour Java
    Réponses: 2
    Dernier message: 22/02/2006, 09h56
  5. Réponses: 1
    Dernier message: 17/06/2005, 11h35

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo