lade
Not just the usual fudge of ClassLoader.defineClass() - loads in-memory JARs returning valid resource URLs
Consists of:
- A self-first URLClassLoader
- An in-memory ClassLoader
- The usual exposure of defineClass()
- Static utility to execute a Runnable within a thread’s context ClassLoader
The io.earcam.instrumental.lade.InMemoryClassLoader:
- Briefly employs the builder pattern to add class and resources
- Loading strategy is self-first
- Load JARs from byte arrays and InputStreams
- Is parallel capable and registered as such
- URLs provide valid streams; either using the instance or, if serializing, once the URLStreamHandler is registered
- Basic support for signed content; CodeSigners associated with loaded classes, but currently archives not verified
- Works as the ClassLoader for:
- Vanilla Java
- JPMS (also supports dynamic layers with custom ModuleFinder)
- the JVM System ClassLoader (“java.system.class.loader”)
- Felix Connect (formerly known as PojoSR)
Examples
Self-first URLClassLoader:
@Test
public void selfFirstClassLoader() throws IOException, ClassNotFoundException
{
Path junitApiJar = Paths.get(Resources.sourceOfResource(Test.class));
try(URLClassLoader selfFirst = ClassLoaders.selfFirstClassLoader(junitApiJar)) {
Class<?> loaded = selfFirst.loadClass(Test.class.getCanonicalName());
assertThat(loaded, is(not(equalTo(Test.class))));
}
}
View full source of associated test.
InMemoryClassLoader (loading byte array JARs in try head):
try(InMemoryClassLoader classLoader = ClassLoaders.inMemoryClassLoader()
.jar(jarOneBytes, "one")
.jar(jarTwoBytes, "two")) {
String name = typeToResourceName(InMemoryClassLoaderTest.class);
Enumeration<URL> resources = classLoader.getResources(name);
List<URL> earls = enumerationToList(resources);
// 3 because the loader also delegates to parent
assertThat(earls, hasSize(3));
}
Orphaned InMemoryClassLoader (loading byte array JARs in try body):
try(InMemoryClassLoader classLoader = ClassLoaders.orphanedInMemoryClassLoader()) {
classLoader.jar(jarOneBytes, "one");
classLoader.jar(jarTwoBytes, "two");
String name = typeToResourceName(InMemoryClassLoaderTest.class);
Enumeration<URL> resources = classLoader.getResources(name);
List<URL> earls = enumerationToList(resources);
// 2 because the loader has no parent
assertThat(earls, hasSize(2));
}
Safely add the URLStreamHandler for InMemoryClassLoader:
io.earcam.instrumental.lade.Handler.addProtocolHandlerSystemProperty();
Safely remove the URLStreamHandler for InMemoryClassLoader:
io.earcam.instrumental.lade.Handler.removeProtocolHandlerSystemProperty();