ver restaurar java_home entorno configurar java classloader java-9 urlclassloader java-10

restaurar - ¿Cómo acceder de forma segura a las URL de todos los archivos de recursos en classpath en Java 9/10?



variables de entorno linux (2)

Aprendimos de las notas de lanzamiento de Java 9 que

El cargador de clases de aplicaciones ya no es una instancia de java.net.URLClassLoader (un detalle de implementación que nunca se especificó en versiones anteriores). El código que asume que ClassLoader :: getSytemClassLoader devuelve un objeto URLClassLoader deberá actualizarse.

Esto rompe el código antiguo, que escanea el classpath de la siguiente manera:

Java <= 8

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

que se ejecuta en un

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

Por lo tanto, para Java 9+ se propuso la siguiente solución como PR en el Apache Ignite Project , que funciona como se pretendía con los ajustes en las opciones de tiempo de ejecución de JVM: --add-opens java.base/jdk.internal.loader=ALL-UNNAMED . Sin embargo, como se menciona en los comentarios a continuación, este PR nunca se fusionó en su rama Maestra.

/* * 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; }

Sin embargo, esto causa algunos dolores de cabeza severos debido al uso de Reflection aquí. Esto es una especie de antipatrón y está estrictamente criticado por el plugin prohibido-apis maven :

Invocación de método prohibido: java.lang.reflect.AccessibleObject # setAccessible (boolean) [El uso de la reflexión para evitar los indicadores de acceso falla con SecurityManagers y probablemente ya no funcionará en las clases de tiempo de ejecución en Java 9]

Pregunta

¿Hay una forma segura de acceder a la lista de todas las URLs recursos en la ruta de clase / módulo, a la que puede acceder el cargador de clases dado, en OpenJDK 9/10 sin usar sun.misc.* Importaciones (por ejemplo, mediante Unsafe )?

ACTUALIZACIÓN (relacionado con los comentarios)

Lo sé, eso puedo hacer

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

para obtener los elementos en el classpath y luego analizarlos en URL s. Sin embargo, hasta donde yo sé, esta propiedad solo devuelve el classpath dado en el momento del lanzamiento de la aplicación. Sin embargo, en un entorno de contenedor, este será el del servidor de aplicaciones y podría no ser suficiente, por ejemplo, utilizando paquetes EAR.

ACTUALIZACIÓN 2

Gracias a todos por todos sus comentarios. Voy a probar, si System.getProperty("java.class.path") funcionará para nuestros propósitos y actualizará la pregunta, si esto satisface nuestras necesidades.

Sin embargo, parece que otros proyectos (tal vez por otras razones, por ejemplo, Apache TomEE 8) sufren el mismo dolor relacionado con el URLClassLoader , por esta razón, creo que sigue siendo una pregunta valiosa.


AFAIK puedes analizar la propiedad del sistema java.class.path para obtener las 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/]


Creo que este es un problema XY . El acceso a las direcciones URL de todos los recursos en classpath no es una operación admitida en Java y no es bueno intentarlo. Como ya has visto en esta pregunta, lucharás contra el marco de trabajo en todo momento si tratas de hacerlo. Habrá un millón de casos extremos que romperán su solución (cargadores de clases personalizados, contenedores EE, etc., etc.).

¿Podría ampliar la razón por la que quiere hacer esto?

Si tiene algún tipo de sistema de complemento y está buscando módulos que interactúen con su código que pueden haber sido proporcionados en tiempo de ejecución, entonces debe usar la API de ServiceLoader , es decir:

Un proveedor de servicios que se empaqueta como un archivo JAR para la ruta de clase se identifica al colocar un archivo de configuración de proveedor en el directorio de recursos META-INF/services . El nombre del archivo de configuración del proveedor es el nombre binario completo del servicio. El archivo de configuración del proveedor contiene una lista de nombres binarios totalmente calificados de proveedores de servicios, uno por línea. Por ejemplo, supongamos que el proveedor de servicios com.example.impl.StandardCodecs se empaqueta en un archivo JAR para la ruta de clase. El archivo JAR contendrá un archivo de configuración del proveedor llamado:

META-INF/services/com.example.CodecFactory

que contiene la línea:

com.example.impl.StandardCodecs # Standard codecs