87 votes

Comment provoquer intentionnellement un message d'avertissement personnalisé du compilateur java ?

Je suis sur le point de commettre un affreux hack temporaire afin de contourner un problème de blocage pendant que nous attendons qu'une ressource externe soit corrigée. En plus de le marquer avec un gros commentaire effrayant et un tas de FIXMEs, j'aimerais que le compilateur affiche un message d'avertissement évident comme rappel pour que nous n'oubliions pas de le retirer. Par exemple, quelque chose comme :

[javac] com.foo.Hacky.java:192: warning: FIXME temporary hack to work around library bug, remove me when library is fixed!

Existe-t-il un moyen de provoquer un avertissement intentionnel du compilateur avec un message de mon choix ? Sinon, quelle est la chose la plus simple à ajouter au code pour lancer un avertissement existant, avec peut-être un message dans une chaîne sur la ligne incriminée pour qu'il soit imprimé dans le message d'avertissement ?

EDIT : Les balises dépréciées ne semblent pas avoir d'effet sur moi :

/**
 * @deprecated "Temporary hack to work around remote server quirks"
 */
@Deprecated
private void doSomeHackyStuff() { ... }

Pas d'erreur de compilation ou d'exécution dans eclipse ou dans sun javac 1.6 (exécution à partir de ant script), et la fonction est bien exécutée.

1 votes

FYI : le @Deprecated ne donne qu'un compilateur avertissement et non une erreur de compilation ou d'exécution. Le code devrait s'exécuter

0 votes

Essayez de l'exécuter avec javac directement. Je pense que Ant cache une partie de la sortie. Ou voyez ma réponse mise à jour ci-dessous pour plus de détails.

87voto

barjak Points 4682

Je pense qu'une annotation personnalisée, qui sera traitée par le compilateur, est la solution. J'écris fréquemment des annotations personnalisées pour faire des choses au moment de l'exécution, mais je n'ai jamais essayé de les utiliser au moment de la compilation. Je ne peux donc que vous donner des indications sur les outils dont vous pourriez avoir besoin :

  • Écrire un type d'annotation personnalisé. Cette page explique comment écrire une annotation.
  • Écrivez un processeur d'annotation, qui traite votre annotation personnalisée pour émettre un avertissement. L'outil qui exécute de tels processeurs d'annotation est appelé APT. Vous pouvez trouver une introduction sur cette page . Je pense que ce dont vous avez besoin dans l'API APT est AnnotationProcessorEnvironment, qui vous permettra d'émettre des avertissements.
  • A partir de Java 6, APT est intégré dans javac. C'est-à-dire que vous pouvez ajouter un processeur d'annotation dans la ligne de commande javac. Cette section du manuel javac vous indiquera comment appeler votre processeur d'annotation personnalisé.

Je ne sais pas si cette solution est vraiment praticable. J'essaierai de la mettre en œuvre moi-même lorsque je trouverai un peu de temps.

Modifier

J'ai réussi à mettre en œuvre ma solution. Et en bonus, j'ai utilisé la fonction de fournisseur de service de java pour simplifier son utilisation. En fait, ma solution est un jar qui contient 2 classes : l'annotation personnalisée et le processeur d'annotation. Pour l'utiliser, il suffit d'ajouter ce jar dans le classpath de votre projet, et d'annoter ce que vous voulez ! Cela fonctionne parfaitement dans mon IDE (NetBeans).

Code de l'annotation :

package fr.barjak.hack;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE})
public @interface Hack {

}

Code du processeur :

package fr.barjak.hack_processor;

import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;

@SupportedAnnotationTypes("fr.barjak.hack.Hack")
public class Processor extends AbstractProcessor {

    private ProcessingEnvironment env;

    @Override
    public synchronized void init(ProcessingEnvironment pe) {
        this.env = pe;
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (!roundEnv.processingOver()) {
            for (TypeElement te : annotations) {
                final Set< ? extends Element> elts = roundEnv.getElementsAnnotatedWith(te);
                for (Element elt : elts) {
                    env.getMessager().printMessage(Kind.WARNING,
                            String.format("%s : thou shalt not hack %s", roundEnv.getRootElements(), elt),
                            elt);
                }
            }
        }
        return true;
    }

}

Pour activer le jar résultant en tant que fournisseur de services, ajoutez le fichier META-INF/services/javax.annotation.processing.Processor dans le jar. Ce fichier est un fichier acsii qui doit contenir le texte suivant :

fr.barjak.hack_processor.Processor

3 votes

+1, Bonne recherche ! C'est définitivement la "bonne façon" de faire (si un test unitaire n'est pas pratique), et cela a l'avantage de se démarquer par rapport aux avertissements habituels.

1 votes

Javac émet un avertissement, mais rien ne se passe dans eclipse( ?)

9 votes

Petite note : il n'est pas nécessaire de remplacer init et définir les env vous pouvez obtenir le ProcessingEnvironment de this.processingEnv puisque c'est protected .

42voto

Kevin Day Points 9446

Une technique que j'ai vue utilisée est de lier cela aux tests unitaires (vous faire test unitaire, non ?). En gros, vous créez un test unitaire qui échoue une fois que la correction des ressources externes est réalisée. Ensuite, vous commentez ce test unitaire pour indiquer aux autres comment défaire votre piratage difficile une fois le problème résolu.

Ce qui est vraiment astucieux dans cette approche, c'est que le déclenchement de l'annulation de votre hack est une correction du problème de base lui-même.

2 votes

J'en ai entendu parler lors de l'une des conférences No Fluff Just Stuff (je ne me souviens plus du nom du présentateur). Je l'ai trouvé plutôt bien conçu. Je recommande vivement ces conférences.

3 votes

J'aimerais voir un exemple de cette approche.

1 votes

La réponse date de 11 ans, mais j'irais même plus loin : commenter les tests unitaires est dangereux. Je créerais un test unitaire qui encapsule le comportement indésirable, de sorte que lorsqu'il est finalement corrigé, la complémentarité est rompue.

12voto

WReach Points 13161

Un bon hack en mérite un autre... J'ai l'habitude de générer des avertissements du compilateur dans le but décrit en introduisant une variable inutilisée dans la méthode hackée, ainsi :

/**
 * @deprecated "Temporary hack to work around remote server quirks"
 */
@Deprecated
private void doSomeHackyStuff() {
    int FIXMEtemporaryHackToWorkAroundLibraryBugRemoveMeWhenLibraryIsFixed;
    ...
}

Cette variable inutilisée générera un avertissement qui (en fonction de votre compilateur) ressemblera à quelque chose comme ceci :

WARNING: The local variable FIXMEtemporaryHackToWorkAroundLibraryBugRemoveMeWhenLibraryIsFixed is never read.

Cette solution n'est pas aussi agréable qu'une annotation personnalisée, mais elle a l'avantage de ne nécessiter aucune préparation préalable (en supposant que le compilateur est déjà configuré pour émettre des avertissements pour les variables inutilisées). Je dirais que cette approche ne convient que pour les hacks à courte durée de vie. Pour les hacks à longue durée de vie, je dirais que l'effort de créer une annotation personnalisée serait justifié.

0 votes

Savez-vous comment activer les avertissements relatifs aux variables inutilisées ? Je construis pour Android avec Gradle à partir de la ligne de commande et je n'obtiens aucun avertissement pour les variables inutilisées. Savez-vous comment l'activer dans build.gradle ?

0 votes

Andreas Désolé, je ne connais pas cet environnement/cette chaîne d'outils. S'il n'y a pas déjà une question SO sur ce sujet, vous pourriez envisager d'en poser une.

5voto

Peter Recore Points 11208

Pourquoi ne pas marquer la méthode ou la classe comme @Deprecated ? documents ici . Notez qu'il existe à la fois un @Deprecated et un @deprecated - la version D majuscule est l'annotation et le d minuscule est la version javadoc. La version javadoc vous permet de spécifier une chaîne arbitraire expliquant ce qui se passe. Mais les compilateurs ne sont pas obligés d'émettre un avertissement lorsqu'ils la voient (bien que beaucoup le fassent). L'annotation devrait toujours provoquer un avertissement, bien que je ne pense pas que vous puissiez y ajouter une explication.

MISE À JOUR : voici le code que je viens de tester avec : Sample.java contient :

public class Sample {
    @Deprecated
    public static void foo() {
         System.out.println("I am a hack");
    }
}

SampleCaller.java contient :

public class SampleCaller{
     public static void main(String [] args) {
         Sample.foo();
     }
}

Lorsque j'exécute "javac Sample.java SampleCaller.java", j'obtiens le résultat suivant :

Note: SampleCaller.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

J'utilise la version 1.6 de javac de Sun. Si vous voulez un honnête avertissement plutôt qu'une simple note, utilisez l'option -Xlint. Peut-être que cela se répercutera correctement dans Ant.

0 votes

Je ne semble pas obtenir d'erreur du compilateur avec @Deprecate ; modifiez mon q avec un code d'exemple.

1 votes

Hmm. votre exemple ne montre que la méthode dépréciée. où utilisez-vous la méthode ? c'est là que l'avertissement apparaîtra.

3 votes

Pour mémoire, @Deprecated ne fonctionne que pour les classes (il est donc inutile pour les méthodes privées).

2voto

Matt Phillips Points 2607

Ici présente un tutoriel sur les annotations et donne en bas de page un exemple de définition de vos propres annotations. Malheureusement, un rapide survol du tutoriel indique que celles-ci ne sont disponibles que dans la javadoc...

Annotations utilisées par le compilateur Il existe trois types d'annotations qui sont prédéfinis par la spécification du langage elle-même : @Deprecated, @Override, et @SuppressWarnings.

Il semble donc que tout ce que vous pouvez faire est d'ajouter une balise @Deprecated que le compilateur imprimera ou d'ajouter une balise personnalisée dans les javadocs qui indique le piratage.

0 votes

Le compilateur émettra également un avertissement disant que la méthode que vous marquez avec @Deprecated est ainsi... Il indiquera à l'utilisateur de quelle méthode il s'agit.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X