45 votes

Comment accéder en toute sécurité aux URL de tous les fichiers de ressources dans le chemin de classe en Java 9/10?

Nous avons appris les notes de version de Java 9 que

L'application du chargeur de classe n'est plus une instance de java.net.URLClassLoader (un détail d'implémentation qui n'a jamais été spécifié dans les versions précédentes). Le Code qui suppose que le chargeur de classe::getSytemClassLoader retourne un URLClassLoader objet devra être mis à jour.

Cela rompt avec l'ancien code, qui scrute le classpath comme suit:

Java <= 8

URL[] ressources = ((URLClassLoader) classLoader).getURLs();

qui s'exécute dans un

java.lang.ClassCastException: 
java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to 
java.base/java.net.URLClassLoader

Donc, pour Java 9+ la solution de contournement suivante a été proposée comme un PR Apache Enflammer Projet, qui fonctionne comme prévu compte tenu des ajustements dans la JVM options d'exécution: --add-opens java.base/jdk.internal.loader=ALL-UNNAMED. Toutefois, comme mentionné dans les commentaires ci-dessous, ce RP n'a jamais été fusionnées dans leur branche Master.

/*
 * Java 9 + Bridge to obtain URLs from classpath...
 */
private static URL[] getURLs(ClassLoader classLoader) {
    URL[] urls = new URL[0];

    try {
        //see https://github.com/apache/ignite/pull/2970
        Class builtinClazzLoader = Class.forName("jdk.internal.loader.BuiltinClassLoader");

        if (builtinClazzLoader != null) {
            Field ucpField = builtinClazzLoader.getDeclaredField("ucp");
            ucpField.setAccessible(true);

            Object ucpObject = ucpField.get(classLoader);
            Class clazz = Class.forName("jdk.internal.loader.URLClassPath");

            if (clazz != null && ucpObject != null) {
                Method getURLs = clazz.getMethod("getURLs");

                if (getURLs != null) {
                    urls = (URL[]) getURLs.invoke(ucpObject);
                }
            }
        }

    } catch (NoSuchMethodException | InvocationTargetException | NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {
        logger.error("Could not obtain classpath URLs in Java 9+ - Exception was:");
        logger.error(e.getLocalizedMessage(), e);
    }
    return urls;
}

Toutefois, cela provoque des maux de tête sévères en raison de l'utilisation de la Réflexion ici. C'est une sorte d'anti-modèle et est strictement critiqué par l' interdit-api de plugin maven:

Interdit d'invocation de méthode: java.lang.de réfléchir.AccessibleObject#setAccessible(boolean) [Réflexion d'utilisation pour travailler autour de l'accès des drapeaux échoue avec SecurityManagers et probablement ne fonctionnera plus sur les classes d'exécution de Java 9]

Question

Est-il sûr moyen d'accéder à la liste de toutes les ressources d' URLs dans la classe / chemin d'accès du module, qui peut être consulté par le chargeur de classe, dans OpenJDK 9/10 sans l'aide d' sun.misc.* des importations (par exemple en utilisant Unsafe)?

Mise à JOUR (pour les commentaires)

Je sais, que je peux faire

 String[] pathElements = System.getProperty("java.class.path").split(System.getProperty("path.separator"));

pour obtenir les éléments dans le chemin de la classe et puis les analyser pour URLs. Cependant, autant que je sache - cette propriété renvoie uniquement le classpath donné au moment du lancement de l'application. Cependant, dans un récipient contenant de l'environnement ce sera la seule de l'application serveur et pourrait ne pas être suffisant, par exemple, puis à l'aide d'OREILLE ensembles.

Mise à JOUR 2

Merci beaucoup pour tous vos commentaires. Je vais tester, si System.getProperty("java.class.path") de travail pour nos fins et de mise à jour de la question, si c'capable de réaliser nos besoins.

Cependant, il semble que d'autres projets (peut-être pour d'autres raisons, l'e.g Apache TomEE 8) subissent les mêmes douleurs liées à l' URLClassLoader- pour cette raison, je pense que c'est une précieuses question.

19voto

Rich Points 1870

Je pense que c'est un XY problème. Accéder à l'Url de toutes les ressources sur le chemin de la classe n'est pas une opération pris en charge en Java et n'est pas une bonne chose pour essayer de faire. Comme vous l'avez déjà vu dans cette question, vous serez la lutte contre le cadre de toutes les façon, si vous essayez de le faire. Il y aura un million de cas de bord qui va briser votre solution (personnalisé chargeurs de classes, EE conteneurs, etc. etc.).

S'il vous plaît pourriez-vous développer sur pourquoi vous voulez faire cela?

Si vous avez une sorte de système de plugin et sont à la recherche pour les modules d'interface avec votre code qui peuvent avoir été fournis au moment de l'exécution, alors vous devriez utiliser le ServiceLoader API, c'est à dire:

Un prestataire de service qui se présente sous la forme d'un fichier JAR, pour le chemin de classe est identifiée par le placement d'un fournisseur-fichier de configuration dans le répertoire des ressources META-INF/services. Le nom de ce fichier de configuration est le complet binaires nom du service. Ce fichier de configuration contient une liste de pleinement qualifié binaire noms des fournisseurs de services, une par ligne. Par exemple, supposons que le fournisseur de service com.example.impl.StandardCodecs il est emballé dans un fichier JAR pour le chemin de classe. Le fichier JAR contient un fournisseur-fichier de configuration nommé:

META-INF/services/com.example.CodecFactory

que contient la ligne:

com.example.impl.StandardCodecs # Standard codecs

9voto

Jorn Vernee Points 18630

AFAIK vous pouvez analyser la propriété système java.class.path pour obtenir les URL:

 String classpath = System.getProperty("java.class.path");
String[] entries = classpath.split(File.pathSeparator);
URL[] result = new URL[entries.length];
for(int i = 0; i < entries.length; i++) {
    result[i] = Paths.get(entries[i]).toAbsolutePath().toUri().toURL();
}

System.out.println(Arrays.toString(result)); // e.g. [file:/J:/WS/Oxygen-Stable/jdk10/bin/]
 

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