Comment cacher l'avertissement "accès réflexif illégal" en java 9 sans argument JVM?

j'ai juste essayé D'exécuter mon serveur avec Java 9 et j'ai reçu le prochain avertissement:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by io.netty.util.internal.ReflectionUtil (file:/home/azureuser/server-0.28.0-SNAPSHOT.jar) to constructor java.nio.DirectByteBuffer(long,int)
WARNING: Please consider reporting this to the maintainers of io.netty.util.internal.ReflectionUtil
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

je voudrais cacher cet avertissement sans ajouter --illegal-access=deny aux options JVM lors du démarrage. Quelque chose comme:

System.setProperty("illegal-access", "deny");

y a-t-il un moyen de faire ça?

toutes les réponses connexes suggérant d'utiliser les options JVM, je voudrais désactiver cela du code. Est-ce possible?

pour clarifier - ma question Est sur tourner cet avertissement à partir du code et non via des arguments/drapeaux JVM comme indiqué dans des questions similaires.

33
demandé sur Nikita Koksharov 2017-09-27 21:38:17

4 réponses

il y a des moyens de désactiver l'avertissement d'accès illégal, mais je ne recommande pas de le faire.

1. Approche Simple

Depuis l'avertissement est imprimé sur le flux d'erreur par défaut, vous pouvez simplement fermer ce flux et rediriger stderr à stdout .

public static void disableWarning() {
    System.err.close();
    System.setErr(System.out);
}

Notes:

  • cette approche fusionne les flux d'erreur et de sortie. Qui peut ne pas être souhaitable dans certains cas.
  • vous ne pouvez pas rediriger un message d'avertissement simplement en appelant System.setErr , puisque la référence au flux d'erreur est sauvegardée dans le champ IllegalAccessLogger.warningStream tôt au bootstrap JVM.

2. Approche compliquée sans changement"

une bonne nouvelle est que sun.misc.Unsafe peut encore être consulté dans JDK 9 sans avertissements. La solution est de réinitialiser interne IllegalAccessLogger avec l'aide de L'API dangereuse.

public static void disableWarning() {
    try {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe u = (Unsafe) theUnsafe.get(null);

        Class cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
        Field logger = cls.getDeclaredField("logger");
        u.putObjectVolatile(cls, u.staticFieldOffset(logger), null);
    } catch (Exception e) {
        // ignore
    }
}

34
répondu apangin 2017-09-27 22:58:19

il y a une autre option qui ne nécessite aucune suppression de flux et qui ne s'appuie pas sur des IPA non documentés ou non pris en charge. En utilisant un agent Java, il est possible de redéfinir les modules pour exporter/ouvrir les paquets requis. Le code ressemble à ceci:

void exportAndOpen(Instrumentation instrumentation) {
  Set<Module> unnamed = 
    Collections.singleton(ClassLoader.getSystemClassLoader().getUnnamedModule());
  ModuleLayer.boot().modules().forEach(module -> instrumentation.redefineModule(
        module,
        unnamed,
        module.getPackages().stream().collect(Collectors.toMap(
          Function.identity(),
          pkg -> unnamed
        )),
        module.getPackages().stream().collect(Collectors.toMap(
           Function.identity(),
           pkg -> unnamed
         )),
         Collections.emptySet(),
         Collections.emptyMap()
  ));
}

vous pouvez maintenant lancer n'importe quel accès illégal sans l'avertissement puisque votre application est contenue dans le module sans nom comme par exemple:

Method method = ClassLoader.class.getDeclaredMethod("defineClass", 
    byte[].class, int.class, int.class);
method.setAccessible(true);

pour mettre la main sur l'instance Instrumentation , vous pouvez soit écrire un agent Java ce qui est assez simple et le spécifier sur la ligne de commande (plutôt que le chemin de classe) en utilisant -javaagent:myjar.jar . Le mandataire ne contiendrait qu'une méthode premain comme suit:

public class MyAgent {
  public static void main(String arg, Instrumentation inst) {
    exportAndOpen(inst);
  }
}

alternativement, vous pouvez attacher dynamiquement en utilisant L'API d'attache qui est rendue accessible commodément par le byte-buddy-agent projet (que j'ai écrit):

exportAndOpen(ByteBuddyAgent.install());

que vous devez appeler avant l'accès illégal. Notez que ceci n'est disponible que sur JDKs et Linux VM alors que vous auriez besoin de fournir L'Byte Buddy agent sur la ligne de commande en tant qu'agent Java si vous en aviez besoin sur d'autres VM. Cela peut être pratique lorsque vous voulez l'auto-fixation sur les machines de test et de développement où les JDK sont généralement installés.

comme autres cela ne devrait être qu'une solution intermédiaire, mais je comprends parfaitement que le comportement actuel interrompe souvent la journalisation sur chenilles et les applications de la console.c'est pourquoi je l'ai utilisé moi-même dans des environnements de production comme une solution à court terme à L'utilisation de Java 9 et tant que je n'ai pas rencontré de problèmes.

la bonne chose, cependant, est que cette solution est robuste vers les futures mises à jour que toute opération, même l'attachement dynamique est légale. Utilisation d'un processus d'aide, Byte Buddy travaille même autour de l'auto-attachement normalement interdit.

9
répondu Rafael Winterhalter 2017-10-23 11:49:16

je ne connais aucun moyen d'obtenir ce que vous demandez. Comme vous l'avez souligné, il faudrait ajouter options de ligne de commande ( --add-opens , cependant, pas --illegal-access=deny ) au lancement de la JVM.

vous avez écrit:

Mon objectif est d'éviter les instructions supplémentaires pour les utilisateurs finaux. Nous avons beaucoup d'utilisateurs avec nos serveurs installés et ce serait un grand inconvénient pour eux.

à première vue, vos exigences ne laissent que conclure que le projet N'est pas prêt pour Java 9. Il devrait honnêtement signaler à ses utilisateurs qu'il faut un peu plus de temps pour être entièrement compatible avec Java 9. C'est totalement ok si tôt après la sortie.

6
répondu Nicolai 2017-10-23 07:08:35

vous pouvez open paquets dans module-info.java ou créer un open module .

par exemple: Étape 5 et 6 de migration de votre projet à Jigsaw étape par étape

module shedlock.example {
    requires spring.context;
    requires spring.jdbc;
    requires slf4j.api;
    requires shedlock.core;
    requires shedlock.spring;
    requires HikariCP;
    requires shedlock.provider.jdbc.template;
    requires java.sql;
    opens net.javacrumbs.shedlockexample to spring.core, spring.beans, spring.context;
}

open module shedlock.example {
    requires spring.context;
    requires spring.jdbc;
    requires slf4j.api;
    requires shedlock.core;
    requires shedlock.spring;
    requires HikariCP;
    requires shedlock.provider.jdbc.template;
    requires java.sql;
}
1
répondu TheKojuEffect 2017-09-28 05:36:02