From 8e02bfef3693db0db1d969c1170de84ff4a90c8e Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 24 Aug 2017 10:11:55 +1000 Subject: [PATCH] Issue #1499 ClasspathPattern module support More efficient implementation that precomputes Path and modules --- .../jetty/webapp/ClasspathPattern.java | 387 ++++++++++++------ 1 file changed, 260 insertions(+), 127 deletions(-) diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java index 1ccc7fd35b3..ae6e3c8523e 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.webapp; import java.io.File; +import java.io.IOException; import java.net.URI; import java.net.URL; import java.nio.file.Path; @@ -32,8 +33,10 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Predicate; +import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.util.ArrayTernaryTrie; import org.eclipse.jetty.util.IncludeExcludeSet; import org.eclipse.jetty.util.TypeUtil; @@ -55,6 +58,7 @@ import org.eclipse.jetty.util.resource.Resource; * the class was loaded *
  • 'file:///some/location.jar' - The URI of a jar file from which * the class was loaded + *
  • 'jrt:/modulename' - A Java9 module name
  • *
  • Any of the above patterns preceeded by '-' will exclude rather than include the match. * * When class is initialized from a classpath pattern string, entries @@ -64,54 +68,24 @@ import org.eclipse.jetty.util.resource.Resource; public class ClasspathPattern extends AbstractSet { private static final Logger LOG = Log.getLogger(ClasspathPattern.class); - - enum Type { PACKAGE, CLASSNAME, LOCATION } private static class Entry { private final String _pattern; private final String _name; private final boolean _inclusive; - private final Type _type; - - Entry(String pattern) + + protected Entry(String name, boolean inclusive) { - _pattern=pattern; - _inclusive = !pattern.startsWith("-"); - _name = _inclusive ? pattern : pattern.substring(1).trim(); - boolean is_location = _name.startsWith("file:") || _name.startsWith("jrt:"); - _type = is_location?Type.LOCATION:(_name.endsWith(".")?Type.PACKAGE:Type.CLASSNAME); - } - - Entry(String name, boolean include) - { - _pattern=include?name:("-"+name); - _inclusive = include; _name = name; - boolean is_location = _name.startsWith("file:") || _name.startsWith("jrt:"); - _type = is_location?Type.LOCATION:(_name.endsWith(".")?Type.PACKAGE:Type.CLASSNAME); + _inclusive = inclusive; + _pattern = inclusive ? _name : ("-"+_name); } - public String getPattern() { return _pattern; } - - public boolean isPackage() - { - return _type==Type.PACKAGE; - } - - public boolean isClassName() - { - return _type==Type.CLASSNAME; - } - - public boolean isLocation() - { - return _type==Type.LOCATION; - } public String getName() { @@ -142,7 +116,68 @@ public class ClasspathPattern extends AbstractSet return _inclusive; } } - + + private static class PackageEntry extends Entry + { + protected PackageEntry(String name, boolean inclusive) + { + super(name, inclusive); + } + } + + private static class ClassEntry extends Entry + { + protected ClassEntry(String name, boolean inclusive) + { + super(name, inclusive); + } + } + + private static class LocationEntry extends Entry + { + private final File _file; + + protected LocationEntry(String name, boolean inclusive) + { + super(name, inclusive); + if (!getName().startsWith("file:")) + throw new IllegalArgumentException(name); + try + { + _file = Resource.newResource(getName()).getFile(); + } + catch(IOException e) + { + throw new RuntimeIOException(e); + } + } + + public File getFile() + { + return _file; + } + } + + private static class ModuleEntry extends Entry + { + private final String _module; + + protected ModuleEntry(String name, boolean inclusive) + { + super(name, inclusive); + if (!getName().startsWith("jrt:")) + throw new IllegalArgumentException(name); + _module = getName().split("/")[1]; + } + + public String getModule() + { + return _module; + } + } + + + public static class ByPackage extends AbstractSet implements Predicate { @@ -176,9 +211,9 @@ public class ClasspathPattern extends AbstractSet public boolean add(Entry entry) { String name = entry.getName(); - if (entry.isClassName()) + if (entry instanceof ClassEntry) name+="$"; - else if (entry.isLocation()) + else if (!(entry instanceof PackageEntry)) throw new IllegalArgumentException(entry.toString()); else if (".".equals(name)) name=""; @@ -206,7 +241,7 @@ public class ClasspathPattern extends AbstractSet } @SuppressWarnings("serial") - public static class ByName extends HashSet implements Predicate + public static class ByClass extends HashSet implements Predicate { private final Map _entries = new HashMap<>(); @@ -231,7 +266,7 @@ public class ClasspathPattern extends AbstractSet @Override public boolean add(Entry entry) { - if (!entry.isClassName()) + if (!(entry instanceof ClassEntry)) throw new IllegalArgumentException(entry.toString()); return _entries.put(entry.getName(),entry)==null; } @@ -248,14 +283,14 @@ public class ClasspathPattern extends AbstractSet public static class ByPackageOrName extends AbstractSet implements Predicate { - private final ByName _byName = new ByName(); + private final ByClass _byClass = new ByClass(); private final ByPackage _byPackage = new ByPackage(); @Override public boolean test(String name) { - return _byPackage.test(name) - || _byName.test(name) ; + return _byPackage.test(name) + || _byClass.test(name) ; } @Override @@ -272,19 +307,21 @@ public class ClasspathPattern extends AbstractSet } @Override - public boolean add(Entry e) + public boolean add(Entry entry) { - if (e.isLocation()) - throw new IllegalArgumentException(); - - if (e.isPackage()) - return _byPackage.add(e); - - // Add class name to packages also as classes act - // as packages for nested classes. - boolean added = _byPackage.add(e); - added = _byName.add(e) || added; - return added; + if (entry instanceof PackageEntry) + return _byPackage.add(entry); + + if (entry instanceof ClassEntry) + { + // Add class name to packages also as classes act + // as packages for nested classes. + boolean added = _byPackage.add(entry); + added = _byClass.add(entry) || added; + return added; + } + + throw new IllegalArgumentException(); } @Override @@ -293,82 +330,166 @@ public class ClasspathPattern extends AbstractSet if (!(o instanceof Entry)) return false; - boolean removed = _byPackage.remove(o); - - if (!((Entry)o).isPackage()) - removed = _byName.remove(o) || removed; - - return removed; + boolean removedPackage = _byPackage.remove(o); + boolean removedClass = _byClass.remove(o); + + return removedPackage || removedClass; } @Override public void clear() { _byPackage.clear(); - _byName.clear(); + _byClass.clear(); } } @SuppressWarnings("serial") - public static class ByLocation extends HashSet implements Predicate + public static class ByLocation extends HashSet implements Predicate { @Override public boolean test(URI uri) { - // TODO this is very inefficient with object creation + if (!uri.getScheme().equals("file")) + return false; + Path path = Paths.get(uri); - switch(uri.getScheme()) + for (Entry entry : this) { - case "file": - { - Path path = Paths.get(uri); - for (URI u: this) - { - if (u.getScheme().equals("file")) - { - File file = new File(u); - if (file.isDirectory()) - { - if (path.startsWith(file.toPath())) - { - return true; - } - } else - { - if (path.equals(file.toPath())) - { - return true; - } - } - } - } - return false; - } + if (!(entry instanceof LocationEntry)) + throw new IllegalStateException(); - case "jrt": - { - String module = uri.getPath().split("/")[1]; - for (URI u: this) - { - if (u.getScheme().equals("jrt")) - { - String m = u.toString().split("/")[1]; - if (module.equals(m)) - return true; - } - } - return false; - } + File file = ((LocationEntry)entry).getFile(); - default: - throw new IllegalStateException("unknown URI scheme: "+uri); + if (file.isDirectory()) + { + if (path.startsWith(file.toPath())) + { + return true; + } + } else + { + if (path.equals(file.toPath())) + { + return true; + } + } } + return false; } } - + + @SuppressWarnings("serial") + public static class ByModule extends HashSet implements Predicate + { + private final ArrayTernaryTrie.Growing _entries = new ArrayTernaryTrie.Growing<>(false,512,512); + + @Override + public boolean test(URI uri) + { + if (!uri.getScheme().equalsIgnoreCase("jrt")) + return false; + String module = uri.getPath(); + int end = module.indexOf('/',1); + if (end<1) + end = module.length(); + return _entries.get(module,1,end-1)!=null; + } + + @Override + public Iterator iterator() + { + return _entries.keySet().stream().map(_entries::get).iterator(); + } + + @Override + public int size() + { + return _entries.size(); + } + + @Override + public boolean add(Entry entry) + { + if (!(entry instanceof ModuleEntry)) + throw new IllegalArgumentException(entry.toString()); + String module = ((ModuleEntry)entry).getModule(); + + if (_entries.get(module)!=null) + return false; + _entries.put(module,entry); + return true; + } + + @Override + public boolean remove(Object entry) + { + if (!(entry instanceof Entry)) + return false; + + return _entries.remove(((Entry)entry).getName())!=null; + } + } + + + public static class ByLocationOrModule extends AbstractSet implements Predicate + { + private final ByLocation _byLocation = new ByLocation(); + private final ByModule _byModule = new ByModule(); + + @Override + public boolean test(URI name) + { + return _byLocation.test(name) || _byModule.test(name); + } + + @Override + public Iterator iterator() + { + Set entries = new HashSet<>(); + entries.addAll(_byLocation); + entries.addAll(_byModule); + return entries.iterator(); + } + + @Override + public int size() + { + return _byLocation.size()+_byModule.size(); + } + + @Override + public boolean add(Entry entry) + { + if (entry instanceof LocationEntry) + return _byLocation.add(entry); + if (entry instanceof ModuleEntry) + return _byModule.add(entry); + + throw new IllegalArgumentException(entry.toString()); + } + + @Override + public boolean remove(Object o) + { + if (o instanceof LocationEntry) + return _byLocation.remove(o); + if (o instanceof ModuleEntry) + return _byModule.remove(o); + return false; + } + + @Override + public void clear() + { + _byLocation.clear(); + _byModule.clear(); + } + } + Map _entries = new HashMap<>(); IncludeExcludeSet _patterns = new IncludeExcludeSet<>(ByPackageOrName.class); - IncludeExcludeSet _locations = new IncludeExcludeSet<>(ByLocation.class); + IncludeExcludeSet _locations = new IncludeExcludeSet<>(ByLocationOrModule.class); public ClasspathPattern() { @@ -388,7 +509,7 @@ public class ClasspathPattern extends AbstractSet { if (name==null) return false; - return add(new Entry(name,true)); + return add(newEntry(name,true)); } public boolean include(String... name) @@ -396,7 +517,7 @@ public class ClasspathPattern extends AbstractSet boolean added = false; for (String n:name) if (n!=null) - added = add(new Entry(n,true)) || added; + added = add(newEntry(n,true)) || added; return added; } @@ -404,7 +525,7 @@ public class ClasspathPattern extends AbstractSet { if (name==null) return false; - return add(new Entry(name,false)); + return add(newEntry(name,false)); } public boolean exclude(String... name) @@ -412,7 +533,7 @@ public class ClasspathPattern extends AbstractSet boolean added = false; for (String n:name) if (n!=null) - added = add(new Entry(n,false)) || added; + added = add(newEntry(n,false)) || added; return added; } @@ -421,7 +542,7 @@ public class ClasspathPattern extends AbstractSet { if (pattern==null) return false; - return add(new Entry(pattern)); + return add(newEntry(pattern)); } public boolean add(String... pattern) @@ -429,30 +550,42 @@ public class ClasspathPattern extends AbstractSet boolean added = false; for (String p:pattern) if (p!=null) - added = add(new Entry(p)) || added; + added = add(newEntry(p)) || added; return added; } - + + protected Entry newEntry(String pattern) + { + if (pattern.startsWith("-")) + return newEntry(pattern.substring(1),false); + return newEntry(pattern,true); + } + + protected Entry newEntry(String name, boolean inclusive) + { + if (name.startsWith("-")) + throw new IllegalStateException(name); + if (name.startsWith("file:")) + return new LocationEntry(name, inclusive); + if (name.startsWith("jrt:")) + return new ModuleEntry(name, inclusive); + if (name.endsWith(".")) + return new PackageEntry(name, inclusive); + return new ClassEntry(name,inclusive); + } + protected boolean add(Entry entry) { if (_entries.containsKey(entry.getPattern())) return false; _entries.put(entry.getPattern(),entry); - if (entry.isLocation()) + if (entry instanceof LocationEntry || entry instanceof ModuleEntry) { - try - { - URI uri = Resource.newResource(entry.getName()).getURI(); - if (entry.isInclusive()) - _locations.include(uri); - else - _locations.exclude(uri); - } - catch (Exception e) - { - throw new IllegalArgumentException(e); - } + if (entry.isInclusive()) + _locations.include(entry); + else + _locations.exclude(entry); } else {