JRE dinâmico: Estressando a máquina

Ao estudar a possibilidade de utilização do WebLogic Server como um Grails EmbeddableServer, substituindo Tomcat ou Jetty na execução de ‘grails run-app’, deparei-me com algumas limitações. Certas características do ambiente de execução Java não estão preparadas para serem alteradas após o início da JVM (ou estão?). Apesar de todo alarde, é apenas software e usá-lo de forma pouco convencional não desencadeará o fim do mundo.

As soluções (truques ou workarounds, se preferirem) a seguir necessitam de um SecurityManager ou de políticas de segurança permissivas o suficiente. Podem ser implementadas em Groovy ou Java “puro”. Basicamente faz-se uso de AcessibleObject.setAcessible e de como algumas classes das bibliotecas base de Java funcionam internamente, em particular as implementações disponíveis com o JDK ou JRE da Oracle/Sun e com o OpenJDK.

1. Adicionar, dinamicamente, paths ao CLASSPATH (java.class.path)

change_classpath.groovy

// groovy change_classpath.groovy

def changeClasspath = {paths ->
    def systemClassLoader = ClassLoader.systemClassLoader
    assert systemClassLoader.metaClass.respondsTo(systemClassLoader, 'addURL', java.net.URL)
    def pathSeparator = File.pathSeparator
    def classpathExtension = new StringBuilder()
    for (path in paths) {
        def lib = new File(path); assert lib.exists()
        systemClassLoader.addURL(lib.toURI().toURL())
        classpathExtension.append(pathSeparator); classpathExtension.append(lib.canonicalPath)
    }
    // Required under certain conditions
    System.setProperty('java.class.path', System.getProperty('java.class.path', '') + classpathExtension.toString())    
}

def classes = ['Lib1', 'Lib2', 'Lib3', 'Lib4']
for (className in classes) {
    try {Class.forName(className); assert false} catch(ClassNotFoundException e) {assert true}
}

def extraPaths = ['lib1.jar', 'lib2classes']
extraPaths.addAll((new File('extraLibs').list() as List).collect {'extraLibs/' + it})
changeClasspath(extraPaths)

for (className in classes) {
    def lib = Class.forName(className)
    lib.doSomething()
    println "${className}.classLoader ${lib.classLoader}"
}

ChangeClasspath.java

// java -ea ChangeClasspath

import java.lang.reflect.Method;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class ChangeClasspath {

    public static void main(String [] args) throws java.net.MalformedURLException, NoSuchMethodException, IllegalAccessException, java.lang.reflect.InvocationTargetException, ClassNotFoundException {
        String [] classes = {"Lib1", "Lib2", "Lib3", "Lib4"};
        for (String className : classes) {
            try {Class.forName(className); assert false;} catch(ClassNotFoundException e) {assert true;}
        }

        List <String> extraPaths = new ArrayList();
        extraPaths.add("lib1.jar"); extraPaths.add("lib2classes");
        for (String path : new File("extraLibs").list()) {
            extraPaths.add("extraLibs/" + path);
        }
        
        changeClasspath(extraPaths);

        for (String className : classes) {
            Class lib = Class.forName(className);
            Method doSomethingMethod = lib.getDeclaredMethod("doSomething");
            doSomethingMethod.invoke(null);
            System.out.println(className + ".classLoader " + lib.getClassLoader());
        }
    }

    private static void changeClasspath(List <String> paths) throws NoSuchMethodException, IllegalAccessException, java.net.MalformedURLException, java.lang.reflect.InvocationTargetException{
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        assert systemClassLoader instanceof java.net.URLClassLoader;

        String pathSeparator = File.pathSeparator;
        
        StringBuilder classpathExtension = new StringBuilder();
        Method addURLMethod = java.net.URLClassLoader.class.getDeclaredMethod("addURL", java.net.URL.class);
        addURLMethod.setAccessible(true);

        for (String path : paths) {
            File lib = new File(path); assert lib.exists();
            addURLMethod.invoke(systemClassLoader, lib.toURI().toURL());
        }
        // Required under certain conditions
        System.setProperty("java.class.path", System.getProperty("java.class.path", "") + classpathExtension.toString());
    }

}


2. Adicionar, dinamicamente, paths ao java.library.path

change_librarypath.groovy

// groovy change_librarypath.groovy

def changeLibraryPath = {paths ->
    def newLibraryPath = new StringBuilder()
    for (newPath in paths) {
        def dir = new File(newPath); assert dir.exists()
        newLibraryPath.append(File.pathSeparator); newLibraryPath.append(dir.canonicalPath)        
    }
    
    System.setProperty('java.library.path', System.getProperty('java.library.path', '') + newLibraryPath.toString())
    ClassLoader.@sys_paths = null
}

def osName = System.properties['os.name'].toLowerCase()
def osArch = System.properties['os.arch'].toLowerCase()

def path = new StringBuilder('native/')
for (os in ['windows', 'linux']) {
    if (osName.contains(os)) {
        path.append(os)
        break
    }
}
path.append(osArch.contains('64')? '/x86_64': '/i686')

try {System.loadLibrary('rxtxSerial'); assert false} catch (UnsatisfiedLinkError e) {assert true}

changeLibraryPath([path.toString()])

//println System.getProperty('java.library.path')
System.loadLibrary('rxtxSerial')

3. Alterar o system class loader (java.system.class.loader) de uma JVM em execução

change_sysclassloader.groovy

// groovy change_sysclassloader.groovy

def changeSystemClassLoader = {
    System.setProperty('java.system.class.loader', 'SpecialClassLoader')
    ClassLoader.@sclSet = false
    ClassLoader.@scl = null
}

def systemClassLoader = ClassLoader.systemClassLoader
systemClassLoader.addURL(new File('.').toURI().toURL())
def specialClassLoaderClass = systemClassLoader.loadClass('SpecialClassLoader')

assert !(specialClassLoaderClass.isAssignableFrom(systemClassLoader.class))

def groovyClassLoader = new GroovyClassLoader(this.class.classLoader)
groovyClassLoader.addURL(new File('scripts').toURI().toURL())

System.properties.put('SpecialClassLoader.systemClassLoader', systemClassLoader)
System.properties.put('SpecialClassLoader.parentClassLoader', groovyClassLoader)
//specialClassLoaderClass.setClassLoaders(systemClassLoader, groovyClassLoader)

changeSystemClassLoader()

systemClassLoader = ClassLoader.systemClassLoader
assert specialClassLoaderClass.isAssignableFrom(systemClassLoader.class)

def script = systemClassLoader.loadClass('Lib')
script.test()

Download do código-fonte.

Referências adicionais

Deixe uma resposta

O seu endereço de email não será publicado Campos obrigatórios são marcados *

*


5 + = treze

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>