BeanArchive.java
package fi.eis.libraries.di.context.deployment;
import fi.eis.libraries.di.logger.LogLevel;
import fi.eis.libraries.di.logger.SimpleLogger;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
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;
/**
* <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
*/
class BeanArchive {
private final List<Class> classes = new ArrayList<>();
private final SimpleLogger logger = new SimpleLogger(this.getClass());
public BeanArchive(URL url) {
initArchive(url);
}
public BeanArchive(URL url, LogLevel logLevel) {
setLogLevel(logLevel);
initArchive(url);
}
public BeanArchive(File file) {
initArchive(file);
}
public BeanArchive(File file, LogLevel logLevel) {
setLogLevel(logLevel);
initArchive(file);
}
public List<Class> getClasses() {
return this.classes;
}
private static final String PROCOTOL_JAR = "jar";
private static final String CLASS_FILE_EXTENSION = ".class";
private void initArchive(URL url) {
initArchive(new File(url.getPath()));
}
private void initArchive(File file) {
if (!file.exists()) {
throw new IllegalArgumentException(
String.format("Doesn't exist: %s", file));
}
try {
logger.debug("Handle path: %s", file.toPath());
if (file.isDirectory()) {
handleDirectory(new DirectoryEntry().setFile(file));
} else {
handleFile(file);
}
} catch (IOException e) {
throw new IllegalStateException("Could not handle path: " + file.toPath(), e);
}
}
/**
* Method to go through all entries in a .jar file on disk
*
* @param file jar file that we want to use basis for dependency injection
* @throws IOException
*/
private void handleFile(File file) throws IOException {
logger.debug("Handle archive file: %s", 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()) {
addIfClass(entry.setName(entries.nextElement().getName()));
}
zip.close();
} catch (ZipException e) {
throw new IllegalArgumentException(e);
}
}
/**
* Recursive method to handle a directory tree of classes loaded runtime.
*
* @param entry an entry in a directory. A file instance that might or might not have a path.
* @throws IOException
*/
private void handleDirectory(DirectoryEntry entry) throws IOException {
String directoryPath = entry.getName();
logger.debug("Handle file %s with a path: %s", entry.getFile(), directoryPath);
boolean hasPath = directoryPath != null;
File[] files = entry.getFile().listFiles();
if (files == null) {
throw new IllegalArgumentException(
String.format("Unable to list directory files: %s", entry.getFile()));
}
for (File childFile : files) {
if (hasPath) {
entry.setPath(directoryPath + "/" + childFile.getName());
} else {
entry.setPath(childFile.getName());
}
entry.setFile(childFile);
if (childFile.isDirectory()) {
handleDirectory(entry);
} else {
addIfClass(entry);
}
}
}
private void addIfClass(Entry entry) {
if (isClass(entry.getName())) {
addClassWithName(filenameToClassname(entry.getName()));
}
}
private void addClassWithName(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) {
throw new IllegalArgumentException("Not found: " + className);
}
}
private boolean isClass(String fileName) {
return fileName.endsWith(CLASS_FILE_EXTENSION);
}
private String filenameToClassname(String fileName) {
return fileName.substring(0, fileName.lastIndexOf(CLASS_FILE_EXTENSION))
.replace('/', '.').replace('\\', '.');
}
private void setLogLevel(LogLevel logLevel) {
this.logger.setLogLevel(logLevel);
}
}