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()); |
} |
} |