BytecodeGroovyClassLoader.java
package org.jbehave.core.configuration.groovy;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Map;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.SourceUnit;
import groovy.lang.GroovyClassLoader;
import groovyjarjarasm.asm.ClassWriter;
/**
* Groovy does not cache the bytecode sequences for generated classes. BytecodeReadingParanamer needs these to get
* paramater names from classes The Groovy compiler does create the debug tables, and they are the same as the ones made
* for a native Java class, so this derived GroovyClassLoader fills in for the missing functionality from the base
* GroovyClassLoader.
*
* <p>Groovy allows a mechanism via a system property to force the dump of bytecode to a (temp) directory, but caching
* the bytecode avoids having to clean up temp directories after the run.</p>
*/
public class BytecodeGroovyClassLoader extends GroovyClassLoader {
private Map<String, byte[]> classBytes = new HashMap<>();
@Override
public InputStream getResourceAsStream(String name) {
if (classBytes.containsKey(name)) {
return new ByteArrayInputStream(classBytes.get(name));
}
return super.getResourceAsStream(name);
}
@Override
protected ClassCollector createCollector(CompilationUnit unit, SourceUnit su) {
// These six lines copied from Groovy itself, with the intention to
// return a subclass
InnerLoader loader = AccessController.doPrivileged(new PrivilegedAction<InnerLoader>() {
@Override
public InnerLoader run() {
return new InnerLoader(BytecodeGroovyClassLoader.this);
}
});
return new BytecodeClassCollector(classBytes, loader, unit, su);
}
public static class BytecodeClassCollector extends ClassCollector {
private final Map<String, byte[]> classBytes;
public BytecodeClassCollector(Map<String, byte[]> classBytes, InnerLoader loader, CompilationUnit unit,
SourceUnit su) {
super(loader, unit, su);
this.classBytes = classBytes;
}
@Override
protected Class<?> onClassNode(ClassWriter classWriter, ClassNode classNode) {
classBytes.put(classNode.getName() + ".class", classWriter.toByteArray());
return super.onClassNode(classWriter, classNode);
}
}
}