You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@netbeans.apache.org by GitBox <gi...@apache.org> on 2018/05/16 21:27:36 UTC

[GitHub] matthiasblaesing closed pull request #537: Maven Indexing Optimizations

matthiasblaesing closed pull request #537: Maven Indexing Optimizations
URL: https://github.com/apache/incubator-netbeans/pull/537
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/maven.indexer/src/org/netbeans/modules/maven/indexer/ClassDependencyIndexCreator.java b/maven.indexer/src/org/netbeans/modules/maven/indexer/ClassDependencyIndexCreator.java
index 931df340a..aef7b28c0 100644
--- a/maven.indexer/src/org/netbeans/modules/maven/indexer/ClassDependencyIndexCreator.java
+++ b/maven.indexer/src/org/netbeans/modules/maven/indexer/ClassDependencyIndexCreator.java
@@ -20,9 +20,6 @@
 package org.netbeans.modules.maven.indexer;
 
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInput;
-import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -33,11 +30,12 @@
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.TreeMap;
 import java.util.TreeSet;
+import java.util.function.Predicate;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.util.logging.Level;
@@ -66,7 +64,6 @@
 import org.netbeans.modules.classfile.ClassName;
 import org.netbeans.modules.maven.indexer.api.NBVersionInfo;
 import org.netbeans.modules.maven.indexer.api.RepositoryQueries.ClassUsage;
-import org.openide.filesystems.FileUtil;
 
 /**
  * Scans classes in (local) JARs for their Java dependencies.
@@ -86,7 +83,8 @@
     /** class/in/this/Jar -> [foreign/Class, other/foreign/Nested$Class] */
     private Map<String,Set<String>> classDeps;
 
-    @Override public void populateArtifactInfo(ArtifactContext context) throws IOException {
+    @Override
+    public void populateArtifactInfo(ArtifactContext context) throws IOException {
         classDeps = null;
         ArtifactInfo ai = context.getArtifactInfo();
         if (ai.getClassifier() != null) {
@@ -100,18 +98,24 @@
             LOG.log(Level.FINER, "no artifact for {0}", ai); // not a big deal, maybe just *.pom (or *.pom + *.nbm) here
             return;
         }
+        if (jar.length() == 0) {
+            LOG.log(Level.FINER, "zero length jar for {0}", ai); // Don't try to index zero length files
+            return;
+        }
         String packaging = ai.getPackaging();
         if (packaging == null || (!packaging.equals("jar") && !isArchiveFile(jar))) {
             LOG.log(Level.FINE, "skipping artifact {0} with unrecognized packaging based on {1}", new Object[] {ai, jar});
             return;
         }
         LOG.log(Level.FINER, "reading {0}", jar);
-        Map<String, byte[]> classfiles = read(jar);
-        classDeps = new HashMap<String, Set<String>>();
-        Set<String> classes = classfiles.keySet();
-        for (Map.Entry<String, byte[]> entry : classfiles.entrySet()) {
-            addDependenciesToMap(entry.getKey(), entry.getValue(), classDeps, classes, jar);
-        }
+        classDeps = new HashMap<>();
+        read(jar, (String name, InputStream stream, Set<String> classes) -> {
+            try {
+                addDependenciesToMap(name, stream, classDeps, classes, jar);
+            } catch (IOException ex) {
+                LOG.log(Level.INFO, "Exception indexing " + jar, ex);
+            }
+        });
     }
 
     // adapted from FileUtil, since we do not want to have to use FileObject's here
@@ -213,6 +217,50 @@ static void search(String className, Indexer indexer, Collection<IndexingContext
         return referrers;
     }
 
+    static final Predicate<String> JDK_CLASS_TEST = new MatchWords(new String[]{
+        "apple/applescript", "apple/laf", "apple/launcher", "apple/security",
+        "com/apple/concurrent", "com/apple/eawt", "com/apple/eio", "com/apple/laf", "com/oracle/net",
+        "com/oracle/nio", "com/oracle/util", "com/oracle/webservices", "com/oracle/xmlns",
+        "com/sun/accessibility", "com/sun/activation", "com/sun/awt", "com/sun/beans", "com/sun/corba",
+        "com/sun/demo", "com/sun/image", "com/sun/imageio", "com/sun/istack", "com/sun/java",
+        "com/sun/java_cup", "com/sun/jmx", "com/sun/jndi", "com/sun/management", "com/sun/media",
+        "com/sun/naming", "com/sun/net", "com/sun/nio", "com/sun/org", "com/sun/rmi", "com/sun/rowset",
+        "com/sun/security", "com/sun/swing", "com/sun/tracing", "com/sun/xml", "java/applet", "java/awt",
+        "java/awt/color", "java/awt/datatransfer", "java/awt/dnd", "java/awt/event", "java/awt/font",
+        "java/awt/geom", "java/awt/im", "java/awt/image", "java/awt/peer", "java/awt/print",
+        "java/beans", "java/beans/beancontext", "java/io", "java/lang", "java/lang/annotation",
+        "java/lang/instrument", "java/lang/invoke", "java/lang/management", "java/lang/ref",
+        "java/lang/reflect", "java/math", "java/net", "java/nio", "java/nio/channels", "java/nio/charset",
+        "java/nio/file", "java/rmi", "java/rmi/activation", "java/rmi/dgc", "java/rmi/registry",
+        "java/rmi/server", "java/security", "java/security/acl", "java/security/cert",
+        "java/security/interfaces", "java/security/spec", "java/sql", "java/text", "java/text/spi", "java/time",
+        "java/time/chrono", "java/time/format", "java/time/temporal", "java/time/zone", "java/util",
+        "java/util/concurrent", "java/util/function", "java/util/jar", "java/util/logging",
+        "java/util/prefs", "java/util/regex", "java/util/spi", "java/util/stream", "java/util/zip",
+        "javax/accessibility", "javax/activation", "javax/activity", "javax/annotation",
+        "javax/annotation/processing", "javax/imageio", "javax/imageio/event", "javax/imageio/metadata",
+        "javax/imageio/plugins", "javax/imageio/spi", "javax/imageio/stream", "javax/jws", "javax/jws/soap",
+        "javax/lang/model", "javax/management", "javax/management/loading",
+        "javax/management/modelmbean", "javax/management/monitor", "javax/management/openmbean",
+        "javax/management/relation", "javax/management/remote", "javax/management/timer", "javax/naming",
+        "javax/naming/directory", "javax/naming/event", "javax/naming/ldap", "javax/naming/spi", "javax/net",
+        "javax/net/ssl", "javax/print", "javax/print/attribute", "javax/print/event", "javax/rmi",
+        "javax/rmi/CORBA", "javax/rmi/ssl", "javax/script", "javax/security/auth",
+        "javax/security/cert", "javax/security/sasl", "javax/smartcardio", "javax/sound/midi",
+        "javax/sound/sampled", "javax/sql", "javax/sql/rowset", "javax/swing", "javax/swing/border",
+        "javax/swing/colorchooser", "javax/swing/event", "javax/swing/filechooser", "javax/swing/plaf",
+        "javax/swing/table", "javax/swing/text", "javax/swing/tree", "javax/swing/undo", "javax/tools",
+        "javax/transaction", "javax/transaction/xa", "javax/xml", "javax/xml/bind", "javax/xml/crypto",
+        "javax/xml/datatype", "javax/xml/namespace", "javax/xml/parsers", "javax/xml/soap",
+        "javax/xml/stream", "javax/xml/transform", "javax/xml/validation", "javax/xml/ws",
+        "javax/xml/xpath", "jdk/internal/cmm", "jdk/internal/instrumentation", "jdk/internal/org",
+        "jdk/internal/util", "jdk/management/cmm", "jdk/management/resource", "jdk/net",
+        "jdk/xml/internal", "org/ietf/jgss", "org/jcp/xml", "org/omg/CORBA", "org/omg/CORBA_2_3",
+        "org/omg/CosNaming", "org/omg/Dynamic", "org/omg/DynamicAny", "org/omg/IOP", "org/omg/Messaging",
+        "org/omg/PortableInterceptor", "org/omg/PortableServer", "org/omg/SendingContext", "org/omg/stub",
+        "org/w3c/dom", "org/xml/sax"
+    });
+
     /**
      * @param referrer a referring class, as {@code pkg/Outer$Inner}
      * @param data its bytecode
@@ -220,32 +268,60 @@ static void search(String className, Indexer indexer, Collection<IndexingContext
      * @param siblings other referring classes in the same artifact (including this one), as {@code pkg/Outer$Inner}
      * @param jar the jar file, for diagnostics
      */
-    private static void addDependenciesToMap(String referrer, byte[] data, Map<String, Set<String>> depsMap, Set<String> siblings, File jar) throws IOException {
-        ClassLoader jre = ClassLoader.getSystemClassLoader().getParent();
+    private static void addDependenciesToMap(String referrer, InputStream data, Map<String, Set<String>> depsMap, Set<String> siblings, File jar) throws IOException {
         int shell = referrer.indexOf('$', referrer.lastIndexOf('/') + 1);
         String referrerTopLevel = shell == -1 ? referrer : referrer.substring(0, shell);
-        for (String referee : dependencies(data, referrer, jar)) {
+        for (String referee : dependencies(data, jar)) {
+            if (referrer.equals(referee)) {
+                continue;
+            }
             if (siblings.contains(referee)) {
                 continue; // in same JAR, not interesting
             }
-            try {
-                jre.loadClass(referee.replace('/', '.')); // XXX ought to cache this result
-                continue; // in JRE, not interesting
-            } catch (ClassNotFoundException x) {
+            if (JDK_CLASS_TEST.test(referee)) {
+                continue;
             }
             Set<String> referees = depsMap.get(referrerTopLevel);
             if (referees == null) {
-                referees = new TreeSet<String>();
+                referees = new HashSet<>();
                 depsMap.put(referrerTopLevel, referees);
             }
             referees.add(referee);
         }
     }
 
-    static Map<String,byte[]> read(File jar) throws IOException {
-        JarFile jf = new JarFile(jar, false);
-        try {
-            Map<String, byte[]> classfiles = new TreeMap<String, byte[]>();
+    @FunctionalInterface
+    interface JarClassEntryConsumer {
+
+        void accept(String name, InputStream classData, Set<String> siblings) throws IOException;
+    }
+
+    // XXX in unit tests, indexing is always single-threaded,
+    // in which case the byte array can be a field instead of
+    // a thread local.  Not clear if that is the case in the IDE.
+    final ThreadLocal<byte[]> BYTES = new ThreadLocal<>();
+    // A reasonable base array size that will accommodate typical
+    // class files, to avoid reallocating more than necessary
+    private static final int MIN_ARRAY_SIZE = 16384;
+
+    byte[] bytes(int size) {
+        // There is a pretty significant performance benefit
+        // to not allocating vast numbers of byte arrays
+        byte[] result = BYTES.get();
+        if (result == null || result.length < size) {
+            result = new byte[Math.max(MIN_ARRAY_SIZE, size)];
+            BYTES.set(result);
+        }
+        return result;
+    }
+
+    void read(File jar, JarClassEntryConsumer consumer) throws IOException {
+        Set<String> classNames = new HashSet<>();
+        try (JarFile jf = new JarFile(jar, false)) {
+            // XXX the original code ignores siblings by first having a list
+            // of the class names.  Getting this before processing JAR entries
+            // means iterating the zip index twice.  Not horrible, but would
+            // be nice to avoid it
             Enumeration<JarEntry> e = jf.entries();
             while (e.hasMoreElements()) {
                 JarEntry entry = e.nextElement();
@@ -254,29 +330,44 @@ private static void addDependenciesToMap(String referrer, byte[] data, Map<Strin
                     continue;
                 }
                 String clazz = name.substring(0, name.length() - 6);
-                ByteArrayOutputStream baos = new ByteArrayOutputStream(Math.max((int) entry.getSize(), 0));
-                InputStream is = jf.getInputStream(entry);
-                try {
-                    FileUtil.copy(is, baos);
-                } finally {
-                    is.close();
+                classNames.add(clazz);
+            }
+            e = jf.entries();
+            while (e.hasMoreElements()) {
+                JarEntry entry = e.nextElement();
+                String name = entry.getName();
+                if (!name.endsWith(".class")) {
+                    continue;
+                }
+                int size = Math.max((int) entry.getSize(), 0);
+                if (size > 0) {
+                    // Parsing is considerably faster if the data is preloaded
+                    // into a byte array, likely due to random access
+                    byte[] target = bytes(size);
+                    try (InputStream in = jf.getInputStream(entry)) {
+                        int pos = 0;
+                        int count = 0;
+                        while (count != -1 && pos < size) {
+                            count = in.read(target, pos, size - pos);
+                            pos += count == -1 ? 0 : count;
+                        }
+                    }
+                    try (InputStream in = new ByteArrayInputStream(target, 0, size)) {
+                        String clazz = name.substring(0, name.length() - 6);
+                        consumer.accept(clazz, in, classNames);
+                    }
                 }
-                classfiles.put(clazz, baos.toByteArray());
             }
-            return classfiles;
         } catch (SecurityException x) {
             throw new IOException(x);
-        } finally {
-            jf.close();
         }
     }
 
     // adapted from org.netbeans.nbbuild.VerifyClassLinkage
-    private static Set<String> dependencies(byte[] data, String clazz, File jar) throws IOException {
-        Set<String> result = new TreeSet<String>();
-        DataInputStream input = new DataInputStream(new ByteArrayInputStream(data));
-        ClassFile cf = new ClassFile(input);
-        
+    private static Collection<String> dependencies(InputStream data, File jar) throws IOException {
+        Set<String> result = new HashSet<String>();
+        ClassFile cf = new ClassFile(data);
+
         Set<ClassName> cl = cf.getAllClassNames();
         for (ClassName className : cl) {
             result.add(className.getInternalName());
@@ -284,15 +375,10 @@ private static void addDependenciesToMap(String referrer, byte[] data, Map<Strin
         return result;
     }
 
-    private static void skip(DataInput input, int bytes) throws IOException {
-        int skipped = input.skipBytes(bytes);
-        if (skipped != bytes) {
-            throw new IOException("Truncated class file");
-        }
-    }
-
-    @Override public Collection<IndexerField> getIndexerFields() {
-        return Arrays.asList(FLD_NB_DEPENDENCY_CLASS);
+    static final List<IndexerField> INDEXER_FIELDS = Collections.singletonList(FLD_NB_DEPENDENCY_CLASS);
+    @Override
+    public Collection<IndexerField> getIndexerFields() {
+        return INDEXER_FIELDS;
     }
 
     /**
diff --git a/maven.indexer/src/org/netbeans/modules/maven/indexer/FastScanner.java b/maven.indexer/src/org/netbeans/modules/maven/indexer/FastScanner.java
new file mode 100644
index 000000000..5b15030bb
--- /dev/null
+++ b/maven.indexer/src/org/netbeans/modules/maven/indexer/FastScanner.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.maven.indexer;
+
+import com.google.inject.Inject;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apache.maven.index.ArtifactContext;
+import org.apache.maven.index.ArtifactContextProducer;
+import org.apache.maven.index.Scanner;
+import org.apache.maven.index.ScanningRequest;
+import org.apache.maven.index.ScanningResult;
+import org.apache.maven.index.context.IndexingContext;
+
+/**
+ * Alternative to Maven's DefaultScanner which ignores files NetBeans will not
+ * be interested in indexing; and publishes scanning requests incrementally,
+ * per-directory, rather than first collecting a tree's worth of artifacts.
+ *
+ * @author Tim Boudreau
+ */
+public class FastScanner
+        implements Scanner {
+
+    private final ArtifactContextProducer artifactContextProducer;
+    private static final Logger LOG = Logger.getLogger(FastScanner.class.getName());
+
+    @Inject
+    public FastScanner(ArtifactContextProducer artifactContextProducer) {
+        this.artifactContextProducer = artifactContextProducer;
+    }
+
+    public ScanningResult scan(ScanningRequest request) {
+        request.getArtifactScanningListener().scanningStarted(request.getIndexingContext());
+        ScanningResult result = new ScanningResult(request);
+        try {
+            scanDirectory(request.getStartingDirectory().toPath(), request);
+        } catch (IOException ex) {
+            LOG.log(Level.WARNING, "Scanning failed", ex);
+        } finally {
+            request.getArtifactScanningListener().scanningFinished(request.getIndexingContext(), result);
+        }
+
+        return result;
+    }
+
+    private void scanDirectory(Path dir, ScanningRequest request) throws IOException {
+        if (dir == null) {
+            return;
+        }
+        Files.walkFileTree(dir, new FileVisitor<Path>() {
+            private final Set<Path> poms = new HashSet<>();
+            private final Set<Path> artifacts = new HashSet<>();
+
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+                poms.clear();
+                artifacts.clear();
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                String nm = file.getFileName().toString();
+                if (nm.endsWith(".pom")) {
+                    poms.add(file);
+                    // txt and sha1 are needed for tests to pass, but not likely useful
+                    // in NetBeans, and will impact performance
+                } else if (nm.endsWith(".jar") || nm.endsWith(".nbm") || nm.endsWith(".txt") || nm.endsWith(".xml")) {
+                    artifacts.add(file);
+                }
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
+                LOG.log(Level.INFO, "Visit failed: " + file, exc);
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+                // Ensure JARs are procssed before POMs - see comments on
+                // DefaultScanner's nested comparator class for why
+                try {
+                    if (!artifacts.isEmpty()) {
+                        for (Path jar : artifacts) {
+                            processFile(jar.toFile(), request);
+                        }
+                    }
+                    if (!poms.isEmpty()) {
+                        for (Path pom : poms) {
+                            processFile(pom.toFile(), request);
+                        }
+                    }
+                } catch (Exception e) {
+                    LOG.log(Level.INFO, "Exception indexing " + artifacts + ", " + poms, e);
+                }
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
+    private void processFile(File file, ScanningRequest request) {
+        IndexingContext context = request.getIndexingContext();
+        ArtifactContext ac = artifactContextProducer.getArtifactContext(context, file);
+
+        if (ac != null) {
+            request.getArtifactScanningListener().artifactDiscovered(ac);
+        }
+    }
+}
diff --git a/maven.indexer/src/org/netbeans/modules/maven/indexer/MatchWords.java b/maven.indexer/src/org/netbeans/modules/maven/indexer/MatchWords.java
new file mode 100644
index 000000000..cff7000ac
--- /dev/null
+++ b/maven.indexer/src/org/netbeans/modules/maven/indexer/MatchWords.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.maven.indexer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * Given multiple strings, this {@link Predicate} tests positive, if one or
+ * multiple of the strings are prefixes of the string, that is tested.
+ *
+ * @author Tim Boudreau
+ */
+final class MatchWords implements Predicate<String> {
+
+    private final List<MatchState> matchers = new ArrayList<>();
+    private ThreadLocal<MatchState[]> local = new ThreadLocal<>();
+
+    MatchWords(String[] strings) {
+        for (String s : strings) {
+            matchers.add(new MatchState(s));
+        }
+    }
+
+    private MatchState[] matchers() {
+        MatchState[] result = local.get();
+        if (result == null) {
+            result = new MatchState[matchers.size()];
+            for (int i = 0; i < result.length; i++) {
+                result[i] = matchers.get(i).copy();
+            }
+            local.set(result);
+        }
+        return result;
+    }
+
+    @Override
+    public boolean test(String t) {
+        int max = t.length();
+        MatchState[] mtchrs = matchers();
+        for (MatchState mtchr : mtchrs) {
+            mtchr.reset();
+        }
+        for (int i = 0; i < max; i++) {
+            char c = t.charAt(i);
+            for (int j = 0; j < mtchrs.length; j++) {
+                mtchrs[j].check(c);
+                if (mtchrs[j].isMatched()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private static final class MatchState {
+
+        private final char[] what;
+        private int matched = 0;
+        private boolean failed = false;
+
+        MatchState(String what) {
+            this.what = what.toCharArray();
+        }
+
+        MatchState(char[] what) {
+            this.what = what;
+        }
+
+        public MatchState copy() {
+            return new MatchState(what);
+        }
+
+        private void reset() {
+            matched = 0;
+            failed = false;
+        }
+
+        boolean isMatched() {
+            return matched >= what.length;
+        }
+
+        void check(char c) {
+            if (failed || isMatched()) {
+                return;
+            }
+            if (what[matched] == c) {
+                matched++;
+            } else {
+                failed = true;
+            }
+        }
+    }
+}
diff --git a/maven.indexer/src/org/netbeans/modules/maven/indexer/NexusRepositoryIndexerImpl.java b/maven.indexer/src/org/netbeans/modules/maven/indexer/NexusRepositoryIndexerImpl.java
index 7affa26dd..0c46109f2 100644
--- a/maven.indexer/src/org/netbeans/modules/maven/indexer/NexusRepositoryIndexerImpl.java
+++ b/maven.indexer/src/org/netbeans/modules/maven/indexer/NexusRepositoryIndexerImpl.java
@@ -41,7 +41,6 @@
 import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.queryparser.classic.QueryParser;
-import org.apache.maven.index.expr.SearchTyped;
 import org.codehaus.plexus.PlexusConstants;
 import org.apache.lucene.search.*;
 import org.apache.lucene.store.FSDirectory;
@@ -252,10 +251,10 @@ private void initIndexer () {
                 desc.addRequirement(req);
                 embedder.addComponentDescriptor(desc);
                 indexer = embedder.lookup(Indexer.class);
-                scanner = embedder.lookup(org.apache.maven.index.Scanner.class);
                 searcher = embedder.lookup(SearchEngine.class);
                 remoteIndexUpdater = embedder.lookup(IndexUpdater.class);
                 contextProducer = embedder.lookup(ArtifactContextProducer.class);
+                scanner = new FastScanner(contextProducer);
                 inited = true;
             } catch (Exception x) {
                 Exceptions.printStackTrace(x);
diff --git a/maven.indexer/test/unit/src/org/netbeans/modules/maven/indexer/ClassDependencyIndexCreatorTest.java b/maven.indexer/test/unit/src/org/netbeans/modules/maven/indexer/ClassDependencyIndexCreatorTest.java
index a61eb2a02..f404a4bc8 100644
--- a/maven.indexer/test/unit/src/org/netbeans/modules/maven/indexer/ClassDependencyIndexCreatorTest.java
+++ b/maven.indexer/test/unit/src/org/netbeans/modules/maven/indexer/ClassDependencyIndexCreatorTest.java
@@ -19,12 +19,17 @@
 
 package org.netbeans.modules.maven.indexer;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.InputStream;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
 import org.netbeans.modules.maven.indexer.api.RepositoryQueries.ClassUsage;
+import org.openide.filesystems.FileUtil;
 import org.openide.util.test.JarBuilder;
 import org.openide.util.test.TestFileUtils;
 
@@ -93,7 +98,12 @@ public void testRead() throws Exception { // #206111
         File jar = TestFileUtils.writeZipFile(new File(getWorkDir(), "x.jar"),
                 // XXX failed to produce a manifest that would generate a SecurityException if loaded with verify=true
                 "pkg/Clazz.class:ABC");
-        Map<String,byte[]> content = ClassDependencyIndexCreator.read(jar);
+        Map<String, byte[]> content = new TreeMap<>();
+        new ClassDependencyIndexCreator().read(jar, (String name, InputStream classData, Set<String> siblings) -> {
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            FileUtil.copy(classData, out);
+            content.put(name, out.toByteArray());
+        });
         assertEquals("[pkg/Clazz]", content.keySet().toString());
         assertEquals("[65, 66, 67]", Arrays.toString(content.get("pkg/Clazz")));
     }
diff --git a/maven.indexer/test/unit/src/org/netbeans/modules/maven/indexer/MatchWordsTest.java b/maven.indexer/test/unit/src/org/netbeans/modules/maven/indexer/MatchWordsTest.java
new file mode 100644
index 000000000..dd571824d
--- /dev/null
+++ b/maven.indexer/test/unit/src/org/netbeans/modules/maven/indexer/MatchWordsTest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.maven.indexer;
+
+import java.util.function.Predicate;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class MatchWordsTest {
+
+    @Test
+    public void testPositiveMatches() {
+        MatchWords mw = new MatchWords(new String[]{"javax/swing", "javax/sql"});
+        assertTrue(mw.test("javax/swing"));
+        assertTrue(mw.test("javax/sql"));
+        assertTrue(mw.test("javax/swing/test"));
+        assertTrue(mw.test("javax/sql/test/more/depth"));
+    }
+
+    @Test
+    public void testNegativeMatches() {
+        MatchWords mw = new MatchWords(new String[]{"javax/swing", "javax/sql"});
+        assertFalse(mw.test("javax/swin"));
+        assertFalse(mw.test("javax/I_SHOULD_NOT_MATCH/sql"));
+    }
+}


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists