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
- Java Tutorial – Understanding Extension Class Loading
- WebLogic Docs – Java ClassLoading
- Java API – ClassLoader.getSystemClassLoader()
- Java API – URLClassLoader.addURL(URL)
- JAVA API – System.loadLibrary(String)
- Setting “java.library.path” programmatically
- Java Books – JNI – Getting Started
- RXTX – Serial and parallel communication for Java
- OTN Foruns – Embedded WebLogic Server
- OTN Foruns – Loading EAR or WAR using a Custom Classloader
- Anúncio desta postagem
