web-dev-qa-db-fra.com

Comment masquer l'avertissement "Accès illégal réfléchi" dans Java 9 sans argument JVM?

Je viens d'essayer de faire tourner mon serveur avec Java 9 et je reçois 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 masquer cet avertissement sans ajouter --illegal-access=deny aux options de la machine virtuelle Java au démarrage. Quelque chose comme:

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

Y'a-t'il un quelconque moyen d'y arriver?

Toutes les réponses connexes suggérant d'utiliser les options de la machine virtuelle Java, j'aimerais désactiver cette option du code. Est-ce possible?

Pour clarifier, ma question concerne la suppression de cet avertissement du code et non via les arguments/indicateurs de la machine virtuelle Java, comme indiqué dans des questions similaires.

48
Dmitriy Dumanskiy

Il existe des moyens de désactiver l'avertissement d'accès illégal, bien que je ne recommande pas de le faire.

1. approche simple

Puisque l'avertissement est imprimé dans le flux d'erreur par défaut, vous pouvez simplement fermer ce flux et rediriger stderr vers stdout.

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

Remarques:

  • Cette approche fusionne les flux d'erreur et de sortie. Cela peut ne pas être souhaitable dans certains cas.
  • Vous ne pouvez pas rediriger le message d'avertissement simplement en appelant System.setErr, car la référence au flux d'erreur est enregistrée dans le champ IllegalAccessLogger.warningStream au début du démarrage de la JVM.

2. approche compliquée sans changer stderr

Une bonne nouvelle est que Sun.misc.Unsafe peut toujours être consulté dans JDK 9 sans avertissements. La solution consiste à réinitialiser IllegalAccessLogger interne à l'aide de l'API Unsafe.

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
    }
}
45
apangin

Une autre option ne nécessite pas de suppression de flux et ne repose pas sur des API non documentées ou non prises en charge. En utilisant un agent Java, il est possible de redéfinir les modules pour exporter/ouvrir les packages requis. Le code pour ceci ressemblerait à 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 exécuter n'importe quel accès illégal sans avertissement, car votre application est contenue dans le module non nommé, par exemple:

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

Afin de mettre la main sur l'instance Instrumentation, vous pouvez écrire un agent Java , ce qui est assez simple et le spécifier sur la ligne de commande (plutôt que sur le chemin d'accès aux classes) à l'aide de -javaagent:myjar.jar. L'agent ne contiendrait qu'une méthode premain comme suit:

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

Vous pouvez également vous connecter de manière dynamique à l’aide de l’API de connexion, rendue facilement accessible par le projet byte-buddy-agent (qui Je suis l'auteur):

exportAndOpen(ByteBuddyAgent.install());

que vous auriez besoin d’appeler avant l’accès illégal. Notez que cette option n'est disponible que sur les JDK et Linux VM, alors que vous devez fournir l'agent Byte Buddy sur la ligne de commande en tant qu'agent Java si vous en avez besoin sur d'autres machines virtuelles. Cela peut être pratique lorsque vous souhaitez l'auto-attachement sur des machines de test et de développement sur lesquelles les JDK sont généralement installés.

Comme d'autres l'ont souligné, cela ne devrait servir que de solution intermédiaire, mais je comprends tout à fait que le comportement actuel casse souvent les robots de journalisation et les applications pour console. C'est pourquoi je l'ai moi-même utilisé dans des environnements de production comme solution à court terme à l'utilisation de Java 9 et si longtemps que je n’ai rencontré aucun problème.

La bonne chose, cependant, est que cette solution est robuste pour les futures mises à jour, car toute opération, même l'attachement dynamique, est légale. À l'aide d'un processus d'assistance, Byte Buddy fonctionne même autour de l'auto-fixation normalement interdite.

12

Je ne connais aucun moyen de réaliser ce que vous demandez. Comme vous l'avez indiqué, vous devez ajouter options de ligne de commande (--add-opens, bien que, pas --illegal-access=deny) au lancement de la machine virtuelle Java.

Tu as é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 gros inconvénient pour eux.

En apparence, vos exigences ne laissent que conclure que le projet n'est pas prêt pour Java 9. Honnêtement, il convient de signaler à ses utilisateurs qu'il faut un peu plus de temps pour être pleinement compatible avec Java 9. . C'est tout à fait correct si tôt après la sortie.

10
Nicolai
import Java.lang.reflect.Field;
import Java.lang.reflect.Method;

public class Main {
    @SuppressWarnings("unchecked")
    public static void disableAccessWarnings() {
        try {
            Class unsafeClass = Class.forName("Sun.misc.Unsafe");
            Field field = unsafeClass.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Object unsafe = field.get(null);

            Method putObjectVolatile = unsafeClass.getDeclaredMethod("putObjectVolatile", Object.class, long.class, Object.class);
            Method staticFieldOffset = unsafeClass.getDeclaredMethod("staticFieldOffset", Field.class);

            Class loggerClass = Class.forName("jdk.internal.module.IllegalAccessLogger");
            Field loggerField = loggerClass.getDeclaredField("logger");
            Long offset = (Long) staticFieldOffset.invoke(unsafe, loggerField);
            putObjectVolatile.invoke(unsafe, loggerClass, offset, null);
        } catch (Exception ignored) {
        }
    }

    public static void main(String[] args) {
        disableAccessWarnings();
    }
}

Cela fonctionne pour moi dans Java 11.

3
Gan

Vous pouvez open packages dans module-info.Java ou créer un open module.

Par exemple: Checkout Step 5 and 6 of Migration de votre projet vers 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
TheKojuEffect