View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.index;
20  
21  import java.io.File;
22  import java.nio.file.Files;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.Date;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import org.apache.lucene.index.Term;
32  import org.apache.lucene.queryparser.classic.ParseException;
33  import org.apache.lucene.search.BooleanQuery;
34  import org.apache.lucene.search.IndexSearcher;
35  import org.apache.lucene.search.PrefixQuery;
36  import org.apache.lucene.search.Query;
37  import org.apache.lucene.search.TermQuery;
38  import org.apache.lucene.store.Directory;
39  import org.apache.lucene.store.FSDirectory;
40  import org.apache.maven.index.context.IndexingContext;
41  import org.apache.maven.index.packer.IndexPacker;
42  import org.apache.maven.index.packer.IndexPackingRequest;
43  import org.apache.maven.index.search.grouping.GAGrouping;
44  import org.apache.maven.index.search.grouping.GGrouping;
45  import org.apache.maven.index.updater.DefaultIndexUpdater;
46  import org.apache.maven.index.updater.IndexUpdateRequest;
47  import org.apache.maven.index.updater.IndexUpdater;
48  import org.junit.Assert;
49  import org.junit.Test;
50  
51  import static org.apache.lucene.search.BooleanClause.*;
52  import static org.hamcrest.CoreMatchers.is;
53  import static org.hamcrest.MatcherAssert.assertThat;
54  import static org.junit.Assert.assertEquals;
55  import static org.junit.Assert.assertFalse;
56  import static org.junit.Assert.assertNotNull;
57  import static org.junit.Assert.assertNull;
58  import static org.junit.Assert.assertTrue;
59  
60  public class FullIndexNexusIndexerTest extends DefaultIndexNexusIndexerTest {
61      @Override
62      protected void prepareNexusIndexer(NexusIndexer nexusIndexer) throws Exception {
63          context = nexusIndexer.addIndexingContext("test-default", "test", repo, indexDir, null, null, FULL_CREATORS);
64  
65          assertNull(context.getTimestamp()); // unknown upon creation
66  
67          nexusIndexer.scan(context);
68  
69          assertNotNull(context.getTimestamp());
70      }
71  
72      @Test
73      public void testSearchGroupedClasses() throws Exception {
74          {
75              Query q = nexusIndexer.constructQuery(MAVEN.CLASSNAMES, "com/thoughtworks/qdox", SearchType.SCORED);
76              GroupedSearchRequest request = new GroupedSearchRequest(q, new GAGrouping());
77              GroupedSearchResponse response = nexusIndexer.searchGrouped(request);
78              Map<String, ArtifactInfoGroup> r = response.getResults();
79  
80              assertEquals(r.toString(), 2, r.size()); // qdox and testng
81  
82              assertTrue(r.containsKey("qdox : qdox"));
83              assertTrue(r.containsKey("org.testng : testng"));
84              assertEquals("qdox : qdox", r.get("qdox : qdox").getGroupKey());
85              assertEquals("org.testng : testng", r.get("org.testng : testng").getGroupKey());
86          }
87  
88          {
89              Query q = nexusIndexer.constructQuery(MAVEN.CLASSNAMES, "com.thoughtworks.qdox", SearchType.SCORED);
90              GroupedSearchRequest request = new GroupedSearchRequest(q, new GAGrouping());
91              GroupedSearchResponse response = nexusIndexer.searchGrouped(request);
92              Map<String, ArtifactInfoGroup> r = response.getResults();
93              assertEquals(r.toString(), 2, r.size());
94  
95              assertTrue(r.containsKey("qdox : qdox"));
96              assertTrue(r.containsKey("org.testng : testng"));
97              assertEquals("qdox : qdox", r.get("qdox : qdox").getGroupKey());
98              assertEquals("org.testng : testng", r.get("org.testng : testng").getGroupKey());
99          }
100 
101         {
102             Query q = nexusIndexer.constructQuery(MAVEN.CLASSNAMES, "thoughtworks", SearchType.SCORED);
103             GroupedSearchRequest request = new GroupedSearchRequest(q, new GAGrouping());
104             GroupedSearchResponse response = nexusIndexer.searchGrouped(request);
105             Map<String, ArtifactInfoGroup> r = response.getResults();
106             assertEquals(r.toString(), 2, r.size());
107             assertTrue(r.containsKey("qdox : qdox"));
108             assertTrue(r.containsKey("org.testng : testng"));
109             assertEquals("qdox : qdox", r.get("qdox : qdox").getGroupKey());
110             assertEquals("org.testng : testng", r.get("org.testng : testng").getGroupKey());
111         }
112 
113         {
114             // an implicit class name wildcard
115             Query q = nexusIndexer.constructQuery(MAVEN.CLASSNAMES, "Logger", SearchType.SCORED);
116             GroupedSearchRequest request = new GroupedSearchRequest(q, new GGrouping());
117             GroupedSearchResponse response = nexusIndexer.searchGrouped(request);
118 
119             Map<String, ArtifactInfoGroup> r = response.getResults();
120             Assert.assertThat(r.toString(), r.size(), is(2));
121 
122             Iterator<ArtifactInfoGroup> it = r.values().iterator();
123 
124             ArtifactInfoGroup ig1 = it.next();
125             assertEquals(r.toString(), "org.slf4j", ig1.getGroupKey());
126 
127             ArtifactInfoGroup ig2 = it.next();
128             assertEquals(r.toString(), "org.testng", ig2.getGroupKey());
129         }
130 
131         {
132             // a lower case search
133             Query q = nexusIndexer.constructQuery(MAVEN.CLASSNAMES, "logger", SearchType.SCORED);
134             GroupedSearchRequest request = new GroupedSearchRequest(q, new GGrouping());
135             GroupedSearchResponse response = nexusIndexer.searchGrouped(request);
136             Map<String, ArtifactInfoGroup> r = response.getResults();
137             assertEquals(r.toString(), 2, r.size());
138 
139             Iterator<ArtifactInfoGroup> it = r.values().iterator();
140 
141             ArtifactInfoGroup ig1 = it.next();
142             assertEquals(r.toString(), "org.slf4j", ig1.getGroupKey());
143 
144             ArtifactInfoGroup ig2 = it.next();
145             assertEquals(r.toString(), "org.testng", ig2.getGroupKey());
146         }
147 
148         {
149             // explicit class name wildcard without terminator
150             // Since 4.0 query starting with * is illegal
151             // Query q = nexusIndexer.constructQuery( MAVEN.CLASSNAMES, "*.Logger", SearchType.SCORED );
152             Query q = nexusIndexer.constructQuery(MAVEN.CLASSNAMES, ".Logger", SearchType.SCORED);
153             GroupedSearchRequest request = new GroupedSearchRequest(q, new GGrouping());
154             GroupedSearchResponse response = nexusIndexer.searchGrouped(request);
155             Map<String, ArtifactInfoGroup> r = response.getResults();
156             assertEquals(r.toString(), 2, r.size());
157             Iterator<ArtifactInfoGroup> it = r.values().iterator();
158             ArtifactInfoGroup ig1 = it.next();
159             assertEquals(r.toString(), "org.slf4j", ig1.getGroupKey());
160             ArtifactInfoGroup ig2 = it.next();
161             assertEquals(r.toString(), "org.testng", ig2.getGroupKey());
162         }
163 
164         {
165             // explicit class name wildcard with terminator
166             // Since 4.0 query starting with * is illegal
167             // Query q = nexusIndexer.constructQuery( MAVEN.CLASSNAMES, "*.Logger ", SearchType.SCORED );
168             Query q = nexusIndexer.constructQuery(MAVEN.CLASSNAMES, ".Logger ", SearchType.SCORED);
169             GroupedSearchRequest request = new GroupedSearchRequest(q, new GGrouping());
170             GroupedSearchResponse response = nexusIndexer.searchGrouped(request);
171             Map<String, ArtifactInfoGroup> r = response.getResults();
172             assertEquals(r.toString(), 2, r.size());
173             Iterator<ArtifactInfoGroup> it = r.values().iterator();
174             ArtifactInfoGroup ig1 = it.next();
175             assertEquals(r.toString(), "org.slf4j", ig1.getGroupKey());
176             ArtifactInfoGroup ig2 = it.next();
177             assertEquals(r.toString(), "org.testng", ig2.getGroupKey());
178         }
179 
180         {
181             // a class name wildcard
182             // Since 4.0 query starting with * is illegal
183             // Query q = nexusIndexer.constructQuery( MAVEN.CLASSNAMES, "*Logger", SearchType.SCORED );
184             Query q = nexusIndexer.constructQuery(MAVEN.CLASSNAMES, "Logger", SearchType.SCORED);
185             GroupedSearchRequest request = new GroupedSearchRequest(q, new GGrouping());
186             GroupedSearchResponse response = nexusIndexer.searchGrouped(request);
187             Map<String, ArtifactInfoGroup> r = response.getResults();
188             // Results are less, since no PREFIX searches anymore!
189             // assertEquals( r.toString(), 3, r.size() );
190             assertEquals(r.toString(), 2, r.size());
191 
192             Iterator<ArtifactInfoGroup> it = r.values().iterator();
193 
194             // Results are less, since no PREFIX searches anymore!
195             // ArtifactInfoGroup ig1 = it.next();
196             // assertEquals( r.toString(), "commons-logging", ig1.getGroupKey() ); // Jdk14Logger and LogKitLogger
197 
198             ArtifactInfoGroup ig2 = it.next();
199             assertEquals(r.toString(), "org.slf4j", ig2.getGroupKey());
200 
201             ArtifactInfoGroup ig3 = it.next();
202             assertEquals(r.toString(), "org.testng", ig3.getGroupKey());
203         }
204 
205         {
206             // exact class name
207             Query q = nexusIndexer.constructQuery(
208                     MAVEN.CLASSNAMES, "org/apache/commons/logging/LogConfigurationException", SearchType.SCORED);
209             GroupedSearchRequest request = new GroupedSearchRequest(q, new GAGrouping());
210             GroupedSearchResponse response = nexusIndexer.searchGrouped(request);
211 
212             Map<String, ArtifactInfoGroup> r = response.getResults();
213             assertEquals(r.toString(), 2, r.size()); // jcl104-over-slf4j and commons-logging
214         }
215 
216         {
217             // implicit class name pattern
218             Query q = nexusIndexer.constructQuery(
219                     MAVEN.CLASSNAMES, "org.apache.commons.logging.LogConfigurationException", SearchType.SCORED);
220             GroupedSearchRequest request = new GroupedSearchRequest(q, new GAGrouping());
221             GroupedSearchResponse response = nexusIndexer.searchGrouped(request);
222 
223             Map<String, ArtifactInfoGroup> r = response.getResults();
224             assertEquals(r.toString(), 2, r.size()); // jcl104-over-slf4j and commons-logging
225         }
226 
227         {
228             // exact class name
229             Query q = nexusIndexer.constructQuery(
230                     MAVEN.CLASSNAMES, "org.apache.commons.logging.LogConfigurationException", SearchType.EXACT);
231             GroupedSearchRequest request = new GroupedSearchRequest(q, new GAGrouping());
232             GroupedSearchResponse response = nexusIndexer.searchGrouped(request);
233 
234             Map<String, ArtifactInfoGroup> r = response.getResults();
235             assertEquals(r.toString(), 2, r.size()); // jcl104-over-slf4j and commons-logging
236         }
237 
238         {
239             // package name prefix
240             Query q = nexusIndexer.constructQuery(MAVEN.CLASSNAMES, "org.apache.commons.logging", SearchType.SCORED);
241             GroupedSearchRequest request = new GroupedSearchRequest(q, new GAGrouping());
242             GroupedSearchResponse response = nexusIndexer.searchGrouped(request);
243 
244             Map<String, ArtifactInfoGroup> r = response.getResults();
245             assertEquals(r.toString(), 2, r.size()); // jcl104-over-slf4j and commons-logging
246         }
247 
248         {
249             // Since 4.0, queries cannot start with '*'
250             // Query q = nexusIndexer.constructQuery( MAVEN.CLASSNAMES, "*slf4j*Logg*", SearchType.SCORED );
251             Query q = nexusIndexer.constructQuery(MAVEN.CLASSNAMES, "slf4j.Logg*", SearchType.SCORED);
252             GroupedSearchRequest request = new GroupedSearchRequest(q, new GAGrouping());
253             GroupedSearchResponse response = nexusIndexer.searchGrouped(request);
254 
255             Map<String, ArtifactInfoGroup> r = response.getResults();
256             // Error is fixed, see below
257             assertEquals(r.toString(), 1, r.size());
258 
259             {
260                 ArtifactInfoGroup ig = r.values().iterator().next();
261                 ArrayList<ArtifactInfo> list1 = new ArrayList<>(ig.getArtifactInfos());
262                 assertEquals(r.toString(), 2, list1.size());
263 
264                 ArtifactInfo ai1 = list1.get(0);
265                 assertEquals("org.slf4j", ai1.getGroupId());
266                 assertEquals("slf4j-api", ai1.getArtifactId());
267                 assertEquals("1.4.2", ai1.getVersion());
268                 ArtifactInfo ai2 = list1.get(1);
269                 assertEquals("org.slf4j", ai2.getGroupId());
270                 assertEquals("slf4j-api", ai2.getArtifactId());
271                 assertEquals("1.4.1", ai2.getVersion());
272             }
273 
274             // this is fixed now, no more false hit
275             // {
276             // // This was error, since slf4j-log4j12 DOES NOT HAVE any class for this search!
277             // ArtifactInfoGroup ig = r.get( "org.slf4j : slf4j-log4j12" );
278             // ArrayList<ArtifactInfo> list = new ArrayList<ArtifactInfo>( ig.getArtifactInfos() );
279             // assertEquals( list.toString(), 1, list.size() );
280             //
281             // ArtifactInfo ai = list.get( 0 );
282             // assertEquals( "org.slf4j", ai.groupId );
283             // assertEquals( "slf4j-log4j12", ai.artifactId );
284             // assertEquals( "1.4.1", ai.version );
285             // }
286         }
287     }
288 
289     @Test
290     public void testSearchArchetypes() throws Exception {
291         Query q = new TermQuery(new Term(ArtifactInfo.PACKAGING, "maven-archetype"));
292         FlatSearchResponse response = nexusIndexer.searchFlat(new FlatSearchRequest(q));
293         Collection<ArtifactInfo> r = response.getResults();
294 
295         assertEquals(4, r.size());
296 
297         Iterator<ArtifactInfo> it = r.iterator();
298         {
299             ArtifactInfo ai = it.next();
300             assertEquals("org.apache.directory.server", ai.getGroupId());
301             assertEquals("apacheds-schema-archetype", ai.getArtifactId());
302             assertEquals("1.0.2", ai.getVersion());
303         }
304         {
305             ArtifactInfo ai = it.next();
306             assertEquals("org.apache.servicemix.tooling", ai.getGroupId());
307             assertEquals("servicemix-service-engine", ai.getArtifactId());
308             assertEquals("3.1", ai.getVersion());
309         }
310         {
311             ArtifactInfo ai = it.next();
312             assertEquals("org.terracotta.maven.archetypes", ai.getGroupId());
313             assertEquals("pojo-archetype", ai.getArtifactId());
314             assertEquals("1.0.3", ai.getVersion());
315         }
316         {
317             ArtifactInfo ai = it.next();
318             assertEquals("proptest", ai.getGroupId());
319             assertEquals("proptest-archetype", ai.getArtifactId());
320             assertEquals("1.0", ai.getVersion());
321         }
322     }
323 
324     @Test
325     public void testIndexTimestamp() throws Exception {
326         final File targetDir = Files.createTempDirectory("testIndexTimestamp").toFile();
327         targetDir.deleteOnExit();
328 
329         final IndexPacker indexPacker = lookup(IndexPacker.class);
330         final IndexSearcher indexSearcher = context.acquireIndexSearcher();
331         try {
332             final IndexPackingRequest request =
333                     new IndexPackingRequest(context, indexSearcher.getIndexReader(), targetDir);
334             indexPacker.packIndex(request);
335         } finally {
336             context.releaseIndexSearcher(indexSearcher);
337         }
338 
339         Thread.sleep(1000L);
340 
341         File newIndex = new File(getBasedir(), "target/test-new");
342 
343         Directory newIndexDir = FSDirectory.open(newIndex.toPath());
344 
345         IndexingContext newContext =
346                 nexusIndexer.addIndexingContext("test-new", "test", null, newIndexDir, null, null, DEFAULT_CREATORS);
347 
348         final IndexUpdater indexUpdater = lookup(IndexUpdater.class);
349         indexUpdater.fetchAndUpdateIndex(
350                 new IndexUpdateRequest(newContext, new DefaultIndexUpdater.FileFetcher(targetDir)));
351 
352         assertEquals(context.getTimestamp().getTime(), newContext.getTimestamp().getTime());
353 
354         assertEquals(context.getTimestamp(), newContext.getTimestamp());
355 
356         // make sure context has the same artifacts
357 
358         Query query = nexusIndexer.constructQuery(MAVEN.GROUP_ID, "qdox", SearchType.SCORED);
359 
360         FlatSearchRequest request = new FlatSearchRequest(query, newContext);
361         FlatSearchResponse response = nexusIndexer.searchFlat(request);
362         Collection<ArtifactInfo> r = response.getResults();
363 
364         assertEquals(2, r.size());
365 
366         List<ArtifactInfo> list = new ArrayList<>(r);
367 
368         assertEquals(2, list.size());
369 
370         ArtifactInfo ai = list.get(0);
371 
372         assertEquals("1.6.1", ai.getVersion());
373 
374         ai = list.get(1);
375 
376         assertEquals("1.5", ai.getVersion());
377 
378         assertEquals("test", ai.getRepository());
379 
380         Date timestamp = newContext.getTimestamp();
381 
382         newContext.close(false);
383 
384         newIndexDir = FSDirectory.open(newIndex.toPath());
385 
386         newContext =
387                 nexusIndexer.addIndexingContext("test-new", "test", null, newIndexDir, null, null, DEFAULT_CREATORS);
388 
389         indexUpdater.fetchAndUpdateIndex(
390                 new IndexUpdateRequest(newContext, new DefaultIndexUpdater.FileFetcher(targetDir)));
391 
392         assertEquals(timestamp, newContext.getTimestamp());
393 
394         newContext.close(true);
395 
396         assertFalse(new File(newIndex, "timestamp").exists());
397     }
398 
399     @Test
400     public void testArchetype() throws Exception {
401         String term = "proptest";
402 
403         Query bq = new PrefixQuery(new Term(ArtifactInfo.GROUP_ID, term));
404         TermQuery tq = new TermQuery(new Term(ArtifactInfo.PACKAGING, "maven-archetype"));
405 
406         FlatSearchResponse response = nexusIndexer.searchFlat(new FlatSearchRequest(new BooleanQuery.Builder()
407                 .add(tq, Occur.MUST)
408                 .add(bq, Occur.FILTER)
409                 .build()));
410 
411         Collection<ArtifactInfo> r = response.getResults();
412 
413         assertEquals(r.toString(), 1, r.size());
414     }
415 
416     @Test
417     public void testArchetypePackaging() throws Exception {
418         Query query = new TermQuery(new Term(ArtifactInfo.PACKAGING, "maven-archetype"));
419         FlatSearchResponse response = nexusIndexer.searchFlat(new FlatSearchRequest(query));
420         assertEquals(response.getResults().toString(), 4, response.getTotalHits());
421     }
422 
423     @Test
424     public void testBrokenJar() throws Exception {
425         Query q = nexusIndexer.constructQuery(MAVEN.ARTIFACT_ID, "brokenjar", SearchType.SCORED);
426 
427         FlatSearchRequest searchRequest = new FlatSearchRequest(q);
428 
429         FlatSearchResponse response = nexusIndexer.searchFlat(searchRequest);
430 
431         Set<ArtifactInfo> r = response.getResults();
432 
433         assertEquals(r.toString(), 1, r.size());
434 
435         ArtifactInfo ai = r.iterator().next();
436 
437         assertEquals("brokenjar", ai.getGroupId());
438         assertEquals("brokenjar", ai.getArtifactId());
439         assertEquals("1.0", ai.getVersion());
440         assertEquals(null, ai.getClassNames());
441     }
442 
443     @Test
444     public void testMissingPom() throws Exception {
445         Query q = nexusIndexer.constructQuery(MAVEN.ARTIFACT_ID, "missingpom", SearchType.SCORED);
446 
447         FlatSearchRequest searchRequest = new FlatSearchRequest(q);
448 
449         FlatSearchResponse response = nexusIndexer.searchFlat(searchRequest);
450 
451         Set<ArtifactInfo> r = response.getResults();
452 
453         assertEquals(r.toString(), 1, r.size());
454 
455         ArtifactInfo ai = r.iterator().next();
456 
457         assertEquals("missingpom", ai.getGroupId());
458         assertEquals("missingpom", ai.getArtifactId());
459         assertEquals("1.0", ai.getVersion());
460         // See Nexus 2318. It should be null for a jar without classes
461         assertNull(ai.getClassNames());
462     }
463 
464     // ==
465 
466     protected IteratorSearchRequest createHighlightedRequest(Field field, String text, SearchType type)
467             throws ParseException {
468         Query q = nexusIndexer.constructQuery(field, text, type);
469 
470         IteratorSearchRequest request = new IteratorSearchRequest(q);
471 
472         request.getMatchHighlightRequests().add(new MatchHighlightRequest(field, q, MatchHighlightMode.HTML));
473 
474         return request;
475     }
476 
477     @Test
478     public void testClassnameSearchNgWithHighlighting() throws Exception {
479         IteratorSearchRequest request = createHighlightedRequest(MAVEN.CLASSNAMES, "Logger", SearchType.SCORED);
480 
481         IteratorSearchResponse response = nexusIndexer.searchIterator(request);
482 
483         for (ArtifactInfo ai : response) {
484             for (MatchHighlight mh : ai.getMatchHighlights()) {
485                 for (String highlighted : mh.getHighlightedMatch()) {
486                     // Logger and LoggerFactory
487                     assertTrue("Class name should be highlighted", highlighted.contains("<B>Logger"));
488                     assertFalse(
489                             "Class name should not contain \"/\" alone (but okay within HTML, see above!)",
490                             highlighted.matches("\\p{Lower}/\\p{Upper}"));
491                     assertFalse(
492                             "Class name should not begin with \".\" or \"/\"",
493                             highlighted.startsWith(".") || highlighted.startsWith("/"));
494                 }
495             }
496         }
497 
498         assertThat(response.getTotalHitsCount(), is(5));
499 
500         assertEquals("found in jcl104-over-slf4j and commons-logging", 5, response.getTotalHits());
501     }
502 
503     @Test
504     public void testGAVSearchNgWithHighlighting() throws Exception {
505         IteratorSearchRequest request = createHighlightedRequest(MAVEN.GROUP_ID, "commons", SearchType.SCORED);
506 
507         IteratorSearchResponse response = nexusIndexer.searchIterator(request);
508 
509         for (ArtifactInfo ai : response) {
510             for (MatchHighlight mh : ai.getMatchHighlights()) {
511                 assertTrue(
512                         "Group ID should be highlighted",
513                         mh.getHighlightedMatch().contains("<B>commons</B>-logging")
514                                 || mh.getHighlightedMatch().contains("<B>commons</B>-cli"));
515             }
516         }
517 
518         assertEquals("found in commons-logging and commons-cli", 15, response.getTotalHits());
519     }
520 }