DeploymentUnitContext.java
package fi.eis.libraries.di;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import fi.eis.libraries.di.SimpleLogger.LogLevel;
/**
* Creation Date: 1.12.2014
* Creation Time: 21:55
* <p>
* This class borrows heavily from JBoss Weld class FileSystemBeanArchiveHandler, which authors are:
* Pete Muir, Marko Luksa, Martin Kouba
* </p>
* <p>
* FileSystemBeanArchiveHandler has been licensed under Apache License,
* Version 2.0. It permits releasing under different license.
* (http://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses)
* </p>
*
* @author eis
*/
public class DeploymentUnitContext extends Context {
private static final String PROCOTOL_JAR = "jar";
private static final String CLASS_FILE_EXTENSION = ".class";
public DeploymentUnitContext(Class sourceClass) {
super();
// a jar or a directory path of whoever initiated us
initUsing(sourceClass);
}
public DeploymentUnitContext(Class sourceClass, LogLevel logLevel) {
super();
setLogLevel(logLevel);
initUsing(sourceClass);
}
public DeploymentUnitContext(File sourceJar) {
super();
initFrom(handle(sourceJar));
}
public DeploymentUnitContext(File sourceJar, LogLevel logLevel) {
super();
setLogLevel(logLevel);
initFrom(handle(sourceJar));
}
private void initUsing(Class sourceClass) {
initFrom(handle(sourceClass.getProtectionDomain().getCodeSource().getLocation()));
}
private void initFrom(BeanArchiveBuilder builder) {
List<Class> classes = builder.getClasses();
logger.debug("got classes " + classes);
super.modules.add(DependencyInjection.classes(classes));
}
public BeanArchiveBuilder handle(URL url) {
return handle(new File(url.getPath()));
}
public BeanArchiveBuilder handle(File file) {
if (!file.exists()) {
throw new IllegalArgumentException(
String.format("Doesn't exist: %s", file));
}
BeanArchiveBuilder builder = new BeanArchiveBuilder();
try {
logger.debug("Handle path: {0}", file.toPath());
if (file.isDirectory()) {
handleDirectory(new DirectoryEntry().setFile(file), builder);
} else {
handleFile(file, builder);
}
} catch (IOException e) {
throw new IllegalStateException("Could not handle path: " + file.toPath(), e);
}
return builder;
}
protected void handleFile(File file, BeanArchiveBuilder builder) throws IOException {
logger.debug("Handle archive file: {0}", file);
try {
ZipFile zip = new ZipFile(file);
Enumeration<? extends ZipEntry> entries = zip.entries();
ZipFileEntry entry = new ZipFileEntry(PROCOTOL_JAR + ":" + file.toURI().toURL().toExternalForm() + "!/");
while (entries.hasMoreElements()) {
add(entry.setName(entries.nextElement().getName()), builder);
}
zip.close();
} catch (ZipException e) {
throw new IllegalArgumentException(e);
}
}
protected void handleDirectory(DirectoryEntry entry, BeanArchiveBuilder builder) throws IOException {
logger.debug("Handle directory: %s", entry.getFile());
File[] files = entry.getFile().listFiles();
if (files == null) {
throw new IllegalArgumentException(
String.format("Unable to list directory files: %s", entry.getFile()));
}
String parentPath = entry.getName();
for (File child : files) {
if (entry.getName() != null) {
entry.setPath(entry.getName() + "/" + child.getName());
} else {
entry.setPath(child.getName());
}
entry.setFile(child);
if (child.isDirectory()) {
handleDirectory(entry, builder);
} else {
add(entry, builder);
}
entry.setPath(parentPath);
}
}
protected void add(Entry entry, BeanArchiveBuilder builder) throws MalformedURLException {
if (isClass(entry.getName())) {
builder.addClass(filenameToClassname(entry.getName()));
}
}
protected boolean isClass(String name) {
return name.endsWith(CLASS_FILE_EXTENSION);
}
private String filenameToClassname(String filename) {
return filename.substring(0, filename.lastIndexOf(CLASS_FILE_EXTENSION))
.replace('/', '.').replace('\\', '.');
}
/**
* An abstraction of a bean archive entry.
*/
protected interface Entry {
String getName();
/**
* @return the URL, most probably lazily created
* @throws MalformedURLException missing protocol or URL parsing error
*/
URL getUrl() throws MalformedURLException;
}
static class BeanArchiveBuilder {
private final List<Class> classes = new ArrayList<>();
public void addClass(String className) {
try {
Class targetClass = Class.forName(className);
// we want instantiable classes, so don't add interfaces or
// abstract classes
if (targetClass.isInterface() || Modifier.isAbstract(targetClass.getModifiers())) {
return;
}
classes.add(targetClass);
} catch (ClassNotFoundException | NoClassDefFoundError e) {
System.out.println("Not found: " + className);
}
}
public List<Class> getClasses() {
return this.classes;
}
}
private static class ZipFileEntry implements Entry {
private String name;
private String archiveUrl;
ZipFileEntry(String archiveUrl) {
this.archiveUrl = archiveUrl;
}
@Override
public String getName() {
return name;
}
ZipFileEntry setName(String name) {
this.name = name;
return this;
}
@Override
public URL getUrl() throws MalformedURLException {
return new URL(archiveUrl + name);
}
}
private static class DirectoryEntry implements Entry {
private String path;
private File file;
@Override
public String getName() {
return path;
}
@Override
public URL getUrl() throws MalformedURLException {
return file.toURI().toURL();
}
public DirectoryEntry setPath(String path) {
this.path = path;
return this;
}
public File getFile() {
return file;
}
public DirectoryEntry setFile(File dir) {
this.file = dir;
return this;
}
}
}