refactored so the extracting is not tangled with the loading the plugin

This commit is contained in:
nbaars 2015-01-04 14:18:48 +01:00
parent c39d673439
commit 34694b01c0
4 changed files with 65 additions and 29 deletions

View File

@ -1,21 +1,43 @@
package org.owasp.webgoat.plugins; package org.owasp.webgoat.plugins;
import org.owasp.webgoat.lessons.AbstractLesson; import org.owasp.webgoat.lessons.AbstractLesson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Plugin { public class Plugin {
private static final Logger logger = LoggerFactory.getLogger(Plugin.class);
private final Class<AbstractLesson> lesson; private final Class<AbstractLesson> lesson;
private final Path pluginDirectory; private final Path pluginDirectory;
public static class PluginLoadingFailure extends RuntimeException {
public PluginLoadingFailure(String message) {
super(message);
}
}
public static class Builder { public static class Builder {
private Path pluginDirectory; private Path pluginDirectory;
private Class lesson; private Class lesson;
private final List<String> loadedClasses = new ArrayList<String>();
public Builder loadClasses(Map<String, byte[]> classes) {
for (Map.Entry<String, byte[]> clazz : classes.entrySet() ) {
loadClass(clazz.getKey(), clazz.getValue());
}
return this;
}
public Builder loadClass(String name, byte[] classFile) { public Builder loadClass(String name, byte[] classFile) {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
@ -26,20 +48,27 @@ public class Plugin {
if (AbstractLesson.class.isAssignableFrom(clazz)) { if (AbstractLesson.class.isAssignableFrom(clazz)) {
this.lesson = clazz; this.lesson = clazz;
} }
loadedClasses.add(clazz.getName());
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
e.printStackTrace(); logger.error("Unable to load class {}", name);
} }
return this; return this;
} }
public Builder setBaseDirectory(Path pluginDirectory) { public Builder setBaseDirectory(Path pluginDirectory) {
this.pluginDirectory = pluginDirectory; this.pluginDirectory = pluginDirectory;
//Find necessary files flag if something went wrong plugin should complain
return this; return this;
} }
public Plugin build() { public Plugin build() {
if ( lesson == null ) {
throw new PluginLoadingFailure(String.format("Lesson class not found, following classes were detected in the plugin: %s",
StringUtils.collectionToCommaDelimitedString(loadedClasses)));
}
return new Plugin(this.lesson, pluginDirectory); return new Plugin(this.lesson, pluginDirectory);
} }
} }
public Plugin(Class<AbstractLesson> lesson, Path pluginDirectory) { public Plugin(Class<AbstractLesson> lesson, Path pluginDirectory) {
@ -52,9 +81,9 @@ public class Plugin {
try { try {
Files.readAllLines(lesson_plans.resolve(this.lesson.getSimpleName() + ".html"), Charset.defaultCharset()); Files.readAllLines(lesson_plans.resolve(this.lesson.getSimpleName() + ".html"), Charset.defaultCharset());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); logger.error("No html found in directory {}", lesson_plans.toString());
} }
return lesson_plans.resolve(this.lesson.getSimpleName() + ".html").toFile().toString(); return "";
} }
public Class<AbstractLesson> getLesson() { public Class<AbstractLesson> getLesson() {

View File

@ -13,10 +13,5 @@ public class PluginClassLoader extends ClassLoader {
return defineClass(name, classFile, 0, classFile.length); return defineClass(name, classFile, 0, classFile.length);
} }
public static void main(String[] args) {
}
} }

View File

@ -13,60 +13,59 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor; import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.Map;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
/** /**
* Extract the zip file and place the files in a temp directory * Extract the wpf file and collect the classes to load and remember the base directory in which we extracted
* * the files (lesson plans etc)
* TODO: should only do the extraction of the zip file return should be the base directory of the extracted
* plugin. The PluginLoader should take care of the loading
*
*/ */
public class PluginExtractor { public class PluginExtractor {
private static final String DIRECTORY = "webgoat"; private static final String DIRECTORY = "webgoat";
private final Path pluginArchive; private final Path pluginArchive;
private final Logger logger = LoggerFactory.getLogger(getClass()); private final Logger logger = LoggerFactory.getLogger(getClass());
private final List<byte[]> classes = new ArrayList<byte[]>(); private final Map<String, byte[]> classes = new HashMap<String, byte[]>();
private Path baseDirectory;
public PluginExtractor(Path pluginArchive) { public PluginExtractor(Path pluginArchive) {
this.pluginArchive = pluginArchive; this.pluginArchive = pluginArchive;
} }
public void extract() {
public Plugin extract() {
final Plugin.Builder pluginBuilder = new Plugin.Builder();
FileSystem zip = null; FileSystem zip = null;
try { try {
zip = createZipFileSystem(); zip = createZipFileSystem();
final Path root = zip.getPath("/"); final Path root = zip.getPath("/");
final Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir"), DIRECTORY); baseDirectory = Paths.get(System.getProperty("java.io.tmpdir"), DIRECTORY);
pluginBuilder.setBaseDirectory(tmpDir);
Files.walkFileTree(root, new SimpleFileVisitor<Path>() { Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override @Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toString().endsWith(".class")) { if (file.toString().endsWith(".class")) {
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();
Files.copy(file, bos); Files.copy(file, bos);
pluginBuilder.loadClass(file.toString(), bos.toByteArray()); classes.put(file.toString(), bos.toByteArray());
} }
Files.copy(file, Paths.get(baseDirectory.toString(), file.toString()), REPLACE_EXISTING);
Files.copy(file, Paths.get(tmpDir.toString(), file.toString()), StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE; return FileVisitResult.CONTINUE;
} }
}); });
return pluginBuilder.build();
} catch (IOException io) { } catch (IOException io) {
logger.error(String.format("Unable to extract: %s", pluginArchive.getFileName()), io); logger.error(String.format("Unable to extract: %s", pluginArchive.getFileName()), io);
} finally { } finally {
closeZipFileSystem(zip); closeZipFileSystem(zip);
} }
}
return pluginBuilder.build(); public Map<String, byte[]> getClasses() {
return this.classes;
}
public Path getBaseDirectory() {
return this.baseDirectory;
} }
private FileSystem createZipFileSystem() throws IOException { private FileSystem createZipFileSystem() throws IOException {

View File

@ -1,5 +1,8 @@
package org.owasp.webgoat.plugins; package org.owasp.webgoat.plugins;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.nio.file.FileVisitResult; import java.nio.file.FileVisitResult;
import java.nio.file.Files; import java.nio.file.Files;
@ -11,6 +14,7 @@ import java.util.List;
public class PluginsLoader { public class PluginsLoader {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Path path; private final Path path;
public PluginsLoader(Path path) { public PluginsLoader(Path path) {
@ -24,13 +28,22 @@ public class PluginsLoader {
@Override @Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
plugins.add(new PluginExtractor(file).extract()); try {
PluginExtractor extractor = new PluginExtractor(file);
extractor.extract();
Plugin.Builder builder = new Plugin.Builder();
builder.loadClasses(extractor.getClasses());
builder.setBaseDirectory(extractor.getBaseDirectory());
plugins.add(builder.build());
} catch (Plugin.PluginLoadingFailure e) {
logger.error("Unable to load plugin, continue reading others...");
}
return FileVisitResult.CONTINUE; return FileVisitResult.CONTINUE;
} }
}); });
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); logger.error("Loading plugins failed", e);
} }
return plugins; return plugins;
} }