Subversion Repositories pub

Compare Revisions

Ignore whitespace Rev 48 → Rev 49

/pluggablejs/trunk/src/net/outlyer/plugins/PluginLocator.java
0,0 → 1,319
package net.outlyer.plugins;
 
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import net.outlyer.plugins.sandboxing.PluginEnvironment;
import net.outlyer.plugins.sandboxing.Sandbox;
 
/**
*
*/
public class PluginLocator {
 
private final List<URI> paths;
private final Library library;
private static final FileFilter jsFileFilter;
private boolean scanned = false;
static {
jsFileFilter = new FileFilter() {
public boolean accept(final File pathname) {
return pathname.isFile() && pathname.canRead() &&
pathname.getName().endsWith(".js");
}
};
}
private static class Library {
private final HashMap<String, List<Sandbox>> byType;
private final HashMap<String, Sandbox> byFilename;
private final HashMap<String, Sandbox> byName;
{
byType = new HashMap<String, List<Sandbox>>();
byFilename = new HashMap<String, Sandbox>();
byName = new HashMap<String, Sandbox>();
}
void add(final String fileName, final BasePluginObject bpo, final Sandbox sbox) {
byName.put(bpo.name, sbox);
byFilename.put(fileName, sbox);
if (byType.get(bpo.type) == null) {
byType.put(bpo.type, new LinkedList<Sandbox>());
}
byType.get(bpo.type).add(sbox);
}
void drop() {
byType.clear();
byFilename.clear();
byName.clear();
}
}
{
paths = new LinkedList<URI>();
library = new Library();
}
public PluginLocator(final URI defaultPath) {
if (null == defaultPath) {
throw new IllegalArgumentException("Null path in list of paths");
}
 
if (defaultPath.getScheme().equals("file")) {
final File path = new File(defaultPath.normalize()).getAbsoluteFile();
if (path.exists() && path.isDirectory() && path.canRead()) {
paths.add(path.toURI().normalize());
}
}
else if (defaultPath.getScheme().equals("jar")) {
final URI normalized = defaultPath.normalize();
if (!normalized.getSchemeSpecificPart().endsWith("/")) {
throw new IllegalArgumentException("Jar file entries must" +
" represent directories");
}
// TODO: Check for existence (Class.getResource())
paths.add(normalized);
}
else {
throw new IllegalArgumentException("Only directories or jar directories are accepted");
}
}
public Sandbox getFirst(final String type, final String name, final String fileName) {
if (null == type && null == name && null == fileName) {
throw new IllegalArgumentException("At least one of type, name or fileName must be non-null");
}
if (!scanned) {
throw new IllegalStateException("Can't retrieve before scanning");
}
final Set<Sandbox> sf = new HashSet<Sandbox>(),
sn = new HashSet<Sandbox>(),
st = new HashSet<Sandbox>();
// All keys are optional but if one of them is not null and yields no
// results then getFirst yields no result
if (null != fileName) {
final Sandbox s = library.byFilename.get(fileName);
if (null == s) {
return null;
}
sf.add(s);
}
if (null != name) {
final Sandbox s = library.byName.get(name);
if (null == s) {
return null;
}
sn.add(s);
}
if (null != type) {
final Collection<Sandbox> byType = library.byType.get(type);
if (byType.isEmpty()) {
return null;
}
st.addAll(byType);
}
Set<Sandbox> inters;
 
if (null != fileName) {
inters = sf;
 
if (null != name) {
assert ! sn.isEmpty();
inters.retainAll(sn);
}
if (!inters.isEmpty() && null != type) {
assert ! st.isEmpty();
inters.retainAll(st);
}
}
else if (null != name) {
assert null == fileName && sf.isEmpty();
inters = sn;
if (null != type) {
assert ! st.isEmpty();
sn.retainAll(st);
}
}
else {
assert null != type && ! st.isEmpty();
assert null == name && sn.isEmpty();
assert null == fileName && sf.isEmpty();
 
inters = st;
}
if (inters.isEmpty()) {
return null;
}
 
return inters.iterator().next();
}
public Collection<Sandbox> getAll(final String type, final String name, final String fileName) {
if (null == type && null == name && null == fileName) {
throw new IllegalArgumentException("At least one of type, name or fileName must be non-null");
}
if (!scanned) {
throw new IllegalStateException("Can't retrieve before scanning");
}
final Set<Sandbox> set = new HashSet<Sandbox>();
if (null != fileName) {
final Sandbox s = library.byFilename.get(fileName);
if (null != s) {
set.add(s);
}
}
if (null != name) {
final Sandbox s = library.byName.get(name);
if (null != s) {
set.add(s);
}
}
if (null != type) {
final Collection<Sandbox> sbox = library.byType.get(type);
if (null != sbox) {
set.addAll(sbox);
}
}
return Collections.unmodifiableSet(set);
}
public void scan(final PluginEnvironment pe) {
if (scanned) {
return;
}
 
for (final URI uri : paths) {
if (uri.getScheme().equals("file")) {
final File dir = new File(uri);
checkDirConsistency(dir);
scan(pe, dir);
}
else {
assert uri.getScheme().equals("jar");
scan(pe, uri);
}
}
 
scanned = true;
}
 
/**
* Scan over a directory
* @param dir Directory to scan
*/
private void scan(final PluginEnvironment pe, final File dir) {
assert dir.isDirectory() && dir.canRead();
for (final File candidateFile : dir.listFiles(jsFileFilter)) {
final URI fileURI = candidateFile.toURI();
try {
final Sandbox sb = pe.createSandbox(fileURI);
final BasePluginObject bpo = sb.getPluginObject();
if (bpo.name == null) {
bpo.name = candidateFile.getName();
}
library.add(candidateFile.getName(), bpo, sb);
}
catch (final Exception e) {
continue;
}
}
}
 
/**
* Scan over a directory inside a jar file
* @param jarUri Directory to scan
* @return LIst of plugins in directory <code>jarUri</code>
*/
private void scan(final PluginEnvironment pe, final URI jarUri) {
assert jarUri.getScheme().equals("jar");
 
try {
final JarURLConnection conn = (JarURLConnection) jarUri.toURL().openConnection();
assert null != conn.getEntryName();
final Enumeration<JarEntry> entries = conn.getJarFile().entries();
final String[] jarURIElements =jarUri.getSchemeSpecificPart().split("!");
assert jarURIElements.length == 2;
final String jarFile = jarURIElements[0]; // = file:....jar
final String root = jarURIElements[1]; // = /path
while (entries.hasMoreElements()) {
final JarEntry je = entries.nextElement();
 
final String url = "/"+je.getName();
if (url.startsWith(root) && !je.isDirectory()) {
// Accepted URI
 
try {
final Sandbox sb = pe.createSandbox(jarUri);
 
final BasePluginObject bpo = sb.getPluginObject();
 
final String fileName = new File(jarUri.getPath()).getName();
if (bpo.name == null) {
bpo.name = fileName;
}
library.add(fileName, bpo, sb);
}
catch (final Exception e) {
continue;
}
}
}
}
catch (IOException e) {
throw new IllegalStateException("Error reading jar file");
}
}
 
/**
* Throws an unchecked exception if something that, on construction, was
* a valid directory, is not anymore.
* @param dir Directory to check
* @throws java.lang.IllegalStateException If the <em>directory</em> is
* no longer a directory or can't be accessed or has ceased to exist.
*/
private static void checkDirConsistency(final File dir)
throws IllegalStateException {
if (!dir.exists() || !dir.isDirectory() || !dir.canRead()) {
throw new IllegalStateException("Permissions of " + dir +
" modified while running");
}
}
public Collection<Sandbox> list() {
if (!scanned) {
throw new IllegalStateException("Can't list before scanning");
}
return Collections.unmodifiableCollection(library.byFilename.values());
}
}