sirius.kernel.Classpath Maven / Gradle / Ivy
Show all versions of sirius-kernel Show documentation
/*
* Made with all the love in the world
* by scireum in Remshalden, Germany
*
* Copyright by scireum GmbH
* http://www.scireum.de - [email protected]
*/
package sirius.kernel;
import com.google.common.base.Objects;
import sirius.kernel.commons.Strings;
import sirius.kernel.health.Log;
import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
* Retrieves a filtered list of resources in the classpath.
*
* This is used by the {@link sirius.kernel.di.Injector} to discover and register all classes in the
* component model. Additionally {@link sirius.kernel.nls.Babelfish} uses this to load all relevant .properties files.
*
* The method used, is to provide a name of a resource which is placed in every component root (jar file etc.) which
* then can be discovered using Class.getResources.
*
* Once a file pattern is given, all files in the classpath are scanned, starting from the detected roots.
*/
public class Classpath {
/**
* Logger used to log problems when scanning the classpath
*/
protected static final Log LOG = Log.get("classpath");
private List componentRoots;
private ClassLoader loader;
private String componentName;
private List customizations;
/**
* Creates a new Classpath, scanning for component roots with the given name
*
* @param loader the class loader used to load the components
* @param componentName the file name to identify component roots
* @param customizations the list of active customizations to filter visible resources
*/
public Classpath(ClassLoader loader, String componentName, List customizations) {
this.loader = loader;
this.componentName = componentName;
this.customizations = customizations;
}
/**
* Returns the class loader used to load the classpath
*
* @return the class loader used by the framework
*/
public ClassLoader getLoader() {
return loader;
}
/**
* Returns all detected component roots
*
* @return a list of URLs pointing to the component roots
*/
public List getComponentRoots() {
if (componentRoots == null) {
try {
componentRoots = Collections.list(loader.getResources(componentName));
componentRoots.sort(Comparator.comparing(URL::toString));
} catch (IOException e) {
LOG.SEVERE(e);
}
}
return componentRoots;
}
/**
* Scans the classpath for files which relative path match the given patter
*
* @param pattern the pattern for the relative path used to filter files
* @return a stream of matching elements
*/
public Stream find(final Pattern pattern) {
return getComponentRoots().stream().flatMap(this::scan).filter(path -> {
if (customizations != null && path.startsWith("customizations")) {
String config = Sirius.getCustomizationName(path);
return customizations.contains(config);
}
return true;
}).map(pattern::matcher).filter(Matcher::matches);
}
/*
* Scans all files below the given root URL. This can handle file:// and jar:// URLs
*/
private Stream scan(URL url) {
List result = new ArrayList<>();
if ("file".equals(url.getProtocol())) {
try {
File file = new File(url.toURI().getPath());
if (!file.isDirectory()) {
file = file.getParentFile();
}
addFiles(file, result, file);
} catch (URISyntaxException e) {
LOG.SEVERE(e);
}
} else if ("jar".equals(url.getProtocol())) {
try {
JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile();
Enumeration e = jar.entries();
while (e.hasMoreElements()) {
JarEntry entry = e.nextElement();
result.add(entry.getName());
}
} catch (IOException e) {
LOG.SEVERE(e);
}
}
return result.stream();
}
/*
* DFS searcher for file / directory based classpath elements
*/
private void addFiles(File file, List collector, File reference) {
if (!file.exists() || !file.isDirectory()) {
return;
}
for (File child : file.listFiles()) {
if (child.isDirectory()) {
addFiles(child, collector, reference);
} else {
String path = buildRelativePath(reference, child);
collector.add(path);
}
}
}
private String buildRelativePath(File reference, File child) {
List path = new ArrayList<>();
File iter = child;
while (iter != null && !Objects.equal(iter, reference)) {
path.add(0, iter.getName());
iter = iter.getParentFile();
}
return Strings.join(path, "/");
}
}