Fixed classloading issues when a lesson contains an inner class. The plugin classloader only expected the lesson to be loaded, now we keep track of all the classes loaded for each plugin. For each class found in the plugin a plugin classloader was created we need one classloader per plugin

Also needed to rewrite the findClass method to lookup the class in the list instead of loading the class from the byte array.
This commit is contained in:
Nanne Baars 2015-05-04 15:25:28 +02:00
parent 6e8d8562d6
commit 264824eb14
2 changed files with 47 additions and 24 deletions

View File

@ -50,27 +50,24 @@ public class Plugin {
} }
public void loadClasses(Map<String, byte[]> classes) { public void loadClasses(Map<String, byte[]> classes) {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
PluginClassLoader pluginClassLoader = new PluginClassLoader(contextClassLoader);
for (Map.Entry<String, byte[]> clazz : classes.entrySet()) { for (Map.Entry<String, byte[]> clazz : classes.entrySet()) {
loadClass(clazz.getKey(), clazz.getValue()); loadClass(pluginClassLoader, clazz.getKey(), clazz.getValue());
} }
if (lesson == null) { if (lesson == null) {
throw new PluginLoadingFailure(String throw new PluginLoadingFailure(String
.format("Lesson class not found, following classes were detected in the plugin: %s", .format("Lesson class not found, following classes were detected in the plugin: %s",
StringUtils.collectionToCommaDelimitedString(classes.keySet()))); StringUtils.collectionToCommaDelimitedString(classes.keySet())));
} }
} }
private void loadClass(String name, byte[] classFile) { private void loadClass(PluginClassLoader pluginClassLoader, String name, byte[] classFile) {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); String realClassName = name.replaceFirst("/", "").replaceAll("/", ".").replaceAll(".class", "");
PluginClassLoader pluginClassLoader = new PluginClassLoader(contextClassLoader, name, classFile);
try { Class clazz = pluginClassLoader.loadClass(realClassName, classFile);
String realClassName = name.replaceFirst("/", "").replaceAll("/", ".").replaceAll(".class", ""); if (AbstractLesson.class.isAssignableFrom(clazz)) {
Class clazz = pluginClassLoader.loadClass(realClassName); this.lesson = clazz;
if (AbstractLesson.class.isAssignableFrom(clazz)) {
this.lesson = clazz;
}
} catch (ClassNotFoundException e) {
logger.error("Unable to load class {}", name, e);
} }
} }
@ -97,7 +94,7 @@ public class Plugin {
Files.copy(file, bos); Files.copy(file, bos);
Path propertiesPath = createPropertiesDirectory(); Path propertiesPath = createPropertiesDirectory();
ResourceBundleClassLoader.setPropertiesPath(propertiesPath); ResourceBundleClassLoader.setPropertiesPath(propertiesPath);
if ( reload ) { if (reload) {
Files.write(propertiesPath.resolve(file.getFileName()), bos.toByteArray(), CREATE, APPEND); Files.write(propertiesPath.resolve(file.getFileName()), bos.toByteArray(), CREATE, APPEND);
} else { } else {
Files.write(propertiesPath.resolve(file.getFileName()), bos.toByteArray(), CREATE, TRUNCATE_EXISTING); Files.write(propertiesPath.resolve(file.getFileName()), bos.toByteArray(), CREATE, TRUNCATE_EXISTING);
@ -117,8 +114,14 @@ public class Plugin {
public void rewritePaths(Path pluginTarget) { public void rewritePaths(Path pluginTarget) {
try { try {
PluginFileUtils.replaceInFiles(this.lesson.getSimpleName() + "_files", pluginTarget.getFileName().toString() + "/plugin/" + this.lesson.getSimpleName() + "/lessonSolutions/en/" + this.lesson.getSimpleName() + "_files", solutionLanguageFiles.values()); PluginFileUtils.replaceInFiles(this.lesson.getSimpleName() + "_files",
PluginFileUtils.replaceInFiles(this.lesson.getSimpleName() + "_files", pluginTarget.getFileName().toString() + "/plugin/" + this.lesson.getSimpleName() + "/lessonPlans/en/" + this.lesson.getSimpleName() + "_files", lessonPlansLanguageFiles.values()); pluginTarget.getFileName().toString() + "/plugin/" + this.lesson
.getSimpleName() + "/lessonSolutions/en/" + this.lesson.getSimpleName() + "_files",
solutionLanguageFiles.values());
PluginFileUtils.replaceInFiles(this.lesson.getSimpleName() + "_files",
pluginTarget.getFileName().toString() + "/plugin/" + this.lesson
.getSimpleName() + "/lessonPlans/en/" + this.lesson.getSimpleName() + "_files",
lessonPlansLanguageFiles.values());
} catch (IOException e) { } catch (IOException e) {
throw new PluginLoadingFailure("Unable to rewrite the paths in the solutions", e); throw new PluginLoadingFailure("Unable to rewrite the paths in the solutions", e);
} }

View File

@ -1,22 +1,42 @@
package org.owasp.webgoat.plugins; package org.owasp.webgoat.plugins;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class PluginClassLoader extends ClassLoader { public class PluginClassLoader extends ClassLoader {
private final List<Class<?>> classes = new ArrayList<>();
private final Logger logger = LoggerFactory.getLogger(Plugin.class); private final Logger logger = LoggerFactory.getLogger(Plugin.class);
private final byte[] classFile;
public PluginClassLoader(ClassLoader parent, String nameOfClass, byte[] classFile) { public Class<?> loadClass(String nameOfClass, byte[] classFile) {
super(parent); Class<?> clazz = defineClass(nameOfClass, classFile, 0, classFile.length);
logger.debug("Creating class loader for {}", nameOfClass); classes.add(clazz);
this.classFile = classFile; return clazz;
} }
public Class findClass(String name) { public PluginClassLoader(ClassLoader contextClassLoader) {
super(contextClassLoader);
}
public Class findClass(final String name) throws ClassNotFoundException {
logger.debug("Finding class " + name); logger.debug("Finding class " + name);
return defineClass(name, classFile, 0, classFile.length); Optional<Class<?>> foundClass = FluentIterable.from(classes)
.firstMatch(new Predicate<Class<?>>() {
@Override
public boolean apply(Class<?> clazz) {
return clazz.getName().equals(name);
}
});
if (foundClass.isPresent()) {
return foundClass.get();
}
throw new ClassNotFoundException("Class " + name + " not found");
} }
} }