Eliminar la carpeta del classpath de Java en tiempo de ejecución

A continuación encontrará un fragmento como ejemplo técnico para demostrar la adición / eliminación de una ruta.

crear los siguientes archivos fuente en cualquier directorio

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Stack;
import sun.misc.URLClassPath;

public class EvilPathDemo {

    public static void addPath(String path) throws Exception {
        URL u = new File(path).toURI().toURL();
        URLClassLoader urlClassLoader = (URLClassLoader)
            ClassLoader.getSystemClassLoader();
        Class<?> urlClass = URLClassLoader.class;
        Method method = urlClass.getDeclaredMethod("addURL",
                new Class[]{URL.class}
        );
        method.setAccessible(true);
        method.invoke(urlClassLoader, new Object[]{u});
    }

    public static void removePath(String path) throws Exception {
        URL url = new File(path).toURI().toURL();
        URLClassLoader urlClassLoader = (URLClassLoader) 
            ClassLoader.getSystemClassLoader();
        Class<?> urlClass = URLClassLoader.class;
        Field ucpField = urlClass.getDeclaredField("ucp");
        ucpField.setAccessible(true);
        URLClassPath ucp = (URLClassPath) ucpField.get(urlClassLoader);
        Class<?> ucpClass = URLClassPath.class;
        Field urlsField = ucpClass.getDeclaredField("urls");
        urlsField.setAccessible(true);
        Stack urls = (Stack) urlsField.get(ucp);
        urls.remove(url);
    }

    public static void main(String[] args) throws Exception {
        String parm = args.length == 1 ? args[0] : "";
        String evilPath = "/tmp";

        String classpath = System.getProperty("java.class.path");
        boolean isEvilPathSet = false;
        for (String path : classpath.split(File.pathSeparator)) {
            if (path.equalsIgnoreCase(evilPath)) {
                System.out.printf("evil path '%s' in classpath%n", evilPath);
                isEvilPathSet = true;
                break;
            }
        }
        if (isEvilPathSet && parm.equalsIgnoreCase("REMOVE")) {
            System.out.printf("evil path '%s' will be removed%n", evilPath);
            removePath(evilPath);
        }
        tryToLoad("Foo");
        if (parm.equalsIgnoreCase("ADD")) {
            System.out.printf("evil path '%s' will be added%n", evilPath);
            addPath(evilPath);
        }
        tryToLoad("Bar");
    }

    private static void tryToLoad(String className) {
        try {
            Class<?> foo = Class.forName(className);
            System.out.printf("class loaded: %s%n", foo.getName());
        } catch (ClassNotFoundException ex) {
            System.out.println(ex);
        }
    }
}

.

public class Foo {
    static {
        System.out.println("I'm foo...");
    }
}

.

public class Bar {
    static {
        System.out.println("I'm bar...");
    }
}

compilarlos de la siguiente manera

javac EvilPathDemo.java
javac -d /tmp Foo.java Bar.java

Durante la prueba intentaremos cargar las clases Fooy Bar.

sin / tmp en el classpath

java -cp . EvilPathDemo
java.lang.ClassNotFoundException: Foo
java.lang.ClassNotFoundException: Bar

agregando / tmp al classpath

java -cp . EvilPathDemo add
java.lang.ClassNotFoundException: Foo
evil path '/tmp' will be added
I'm bar...
class loaded: Bar

con / tmp en el classpath

java -cp .:/tmp EvilPathDemo
evil path '/tmp' in the classpath
I'm foo...
class loaded: Foo
I'm bar...
class loaded: Bar

eliminar / tmp del classpath

java -cp .:/tmp EvilPathDemo remove
evil path '/tmp' in the classpath
evil path '/tmp' will be removed
java.lang.ClassNotFoundException: Foo
java.lang.ClassNotFoundException: Bar

Durante las pruebas descubrí que los siguientes casos no funcionan.

  • addPath (evilPath);
    tryToLoad ("Foo");
    removePath (evilPath); // no tuvo efecto
    tryToLoad ("Bar");
  • removePath (evilPath);
    tryToLoad ("Foo");
    addPath (evilPath); // no tuvo efecto
    tryToLoad ("Bar");
  • tryToLoad ("Foo");
    removePath (evilPath); // no tuvo efecto
    tryToLoad ("Bar");

No pasé tiempo para averiguar por qué. Porque no veo ningún uso práctico en ello. Si realmente necesitas / deseas jugar con los classpaths, mira cómo funcionan los cargadores de clases.

Respuesta 1

El removePathmétodo de arriba no funcionó para mí y mi Contenedor de soldadura, la pila de URL siempre estaba vacía. El siguiente método feo y petulante funcionó:

public static void removeLastClasspathEntry() throws Exception {
    URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
    Class<?> urlClass = URLClassLoader.class;
    Field ucpField = urlClass.getDeclaredField("ucp");
    ucpField.setAccessible(true);
    URLClassPath ucp = (URLClassPath) ucpField.get(urlClassLoader);

    Field loadersField = URLClassPath.class.getDeclaredField("loaders");
    loadersField.setAccessible(true);
    List jarEntries = (List) loadersField.get(ucp);
    jarEntries.remove(jarEntries.size() - 1);

    Field pathField = URLClassPath.class.getDeclaredField("path");
    pathField.setAccessible(true);
    List pathList = (List) pathField.get(ucp);
    URL jarUrl = (URL) pathList.get(pathList.size() - 1);
    String jarName = jarUrl.toString();
    pathList.remove(pathList.size() - 1);

    Field lmapField = URLClassPath.class.getDeclaredField("lmap");
    lmapField.setAccessible(true);
    Map lmapMap = (Map) lmapField.get(ucp);
    lmapMap.remove(jarName.replaceFirst("file:/", "file:///"));
}
Respuesta: 2

Los cargadores de clases se pueden anidar, por lo que en lugar de modificar el cargador de clases del sistema, que es la raíz del árbol de cargadores de clases, es mejor simplemente crear un cargador de clases anidado y usarlo para cargar clases.

El cargador de clases del sistema en sí es inmutable (por buenas razones), pero puede hacer lo que quiera en cargadores de clases anidados, incluso destruirlos para descargar clases y recursos. Esto se usa comúnmente en, por ejemplo, osgi y servidores de aplicaciones para cargar / descargar, por ejemplo, complementos, aplicaciones, etc.

Para los cargadores de clases anidados, puede personalizar completamente cómo cargar clases. El URLClassloaderes probablemente un buen punto de partida para lo que quieres.

Respuesta: 3

No creo que haya una manera directa de hacerlo. Puedes seguir:

  • Obtener variables de ruta de clase mediante: System.getenv("CLASSPATH"). Devolverá valores separados por punto y coma.

    String classPath = System.getenv("CLASSPATH")

  • Tome la ruta de la carpeta como entrada y reemplácela con "" como:

    String remainigPath = classPath.replace(inputpath,"");

  • Coloque las rutas restantes en una matriz utilizando el método de división.

    String[] paths = remainigPath .split(";");

  • Para agregar classPath, ya tienes el código.

Respuesta: 4

Estamos migrando nuestra aplicación web de Java 7 a Java 8. Habíamos definido los argumentos param jvm de PermSize. Como Java 8 ignorará estos parámetros, esto no es de utilidad. Se introduce Metaspace ...

Me he referido a esto antes de publicar esta pregunta. Verificación de envoltorios nulos contra valores primitivos Y tengo una situación en la que quiero verificar el Wrapper Integer con nulo también 0 if (statusId! = Null ...

Antes de Java8, usaba la clase DateTime de Joda para incluir información de zona horaria y puedo convertir fácilmente entre DateTime y sql Timestamp. Una vez que migre a Java8, ¿qué clase debería reemplazar? ...

Tengo un problema muy divertido hoy. Estoy tratando de modificar una matriz, pero el método modifica otra matriz en la que ni siquiera se invoca. a) Parte del método que está causando problemas public static void place2 (...