View Javadoc
1   package org.apache.maven.index.examples;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.lucene.document.Document;
23  import org.apache.lucene.index.IndexReader;
24  import org.apache.lucene.index.MultiBits;
25  import org.apache.lucene.search.BooleanClause.Occur;
26  import org.apache.lucene.search.BooleanQuery;
27  import org.apache.lucene.search.IndexSearcher;
28  import org.apache.lucene.search.Query;
29  import org.apache.lucene.util.Bits;
30  import org.apache.maven.index.ArtifactInfo;
31  import org.apache.maven.index.ArtifactInfoFilter;
32  import org.apache.maven.index.ArtifactInfoGroup;
33  import org.apache.maven.index.Field;
34  import org.apache.maven.index.FlatSearchRequest;
35  import org.apache.maven.index.FlatSearchResponse;
36  import org.apache.maven.index.GroupedSearchRequest;
37  import org.apache.maven.index.GroupedSearchResponse;
38  import org.apache.maven.index.Grouping;
39  import org.apache.maven.index.Indexer;
40  import org.apache.maven.index.IteratorSearchRequest;
41  import org.apache.maven.index.IteratorSearchResponse;
42  import org.apache.maven.index.MAVEN;
43  import org.apache.maven.index.context.IndexCreator;
44  import org.apache.maven.index.context.IndexUtils;
45  import org.apache.maven.index.context.IndexingContext;
46  import org.apache.maven.index.expr.SourcedSearchExpression;
47  import org.apache.maven.index.expr.UserInputSearchExpression;
48  import org.apache.maven.index.search.grouping.GAGrouping;
49  import org.apache.maven.index.updater.IndexUpdateRequest;
50  import org.apache.maven.index.updater.IndexUpdateResult;
51  import org.apache.maven.index.updater.IndexUpdater;
52  import org.apache.maven.index.updater.ResourceFetcher;
53  import org.apache.maven.index.updater.WagonHelper;
54  import org.apache.maven.wagon.Wagon;
55  import org.apache.maven.wagon.events.TransferEvent;
56  import org.apache.maven.wagon.events.TransferListener;
57  import org.apache.maven.wagon.observers.AbstractTransferListener;
58  import org.codehaus.plexus.DefaultContainerConfiguration;
59  import org.codehaus.plexus.DefaultPlexusContainer;
60  import org.codehaus.plexus.PlexusConstants;
61  import org.codehaus.plexus.PlexusContainer;
62  import org.codehaus.plexus.PlexusContainerException;
63  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
64  import org.codehaus.plexus.util.StringUtils;
65  import org.eclipse.aether.util.version.GenericVersionScheme;
66  import org.eclipse.aether.version.InvalidVersionSpecificationException;
67  import org.eclipse.aether.version.Version;
68  
69  import java.io.File;
70  import java.io.IOException;
71  import java.time.Duration;
72  import java.time.Instant;
73  import java.util.ArrayList;
74  import java.util.Collections;
75  import java.util.Date;
76  import java.util.List;
77  import java.util.Map;
78  
79  /**
80   * Collection of some use cases.
81   */
82  public class BasicUsageExample
83  {
84      public static void main( String[] args )
85          throws Exception
86      {
87          new BasicUsageExample().perform();
88      }
89  
90      // ==
91  
92      private final PlexusContainer plexusContainer;
93  
94      private final Indexer indexer;
95  
96      private final IndexUpdater indexUpdater;
97  
98      private final Wagon httpWagon;
99  
100     private IndexingContext centralContext;
101 
102     public BasicUsageExample()
103         throws PlexusContainerException, ComponentLookupException
104     {
105         // here we create Plexus container, the Maven default IoC container
106         // Plexus falls outside of MI scope, just accept the fact that
107         // MI is a Plexus component ;)
108         // If needed more info, ask on Maven Users list or Plexus Users list
109         // google is your friend!
110         final DefaultContainerConfiguration config = new DefaultContainerConfiguration();
111         config.setClassPathScanning( PlexusConstants.SCANNING_INDEX );
112         this.plexusContainer = new DefaultPlexusContainer( config );
113 
114         // lookup the indexer components from plexus
115         this.indexer = plexusContainer.lookup( Indexer.class );
116         this.indexUpdater = plexusContainer.lookup( IndexUpdater.class );
117         // lookup wagon used to remotely fetch index
118         this.httpWagon = plexusContainer.lookup( Wagon.class, "http" );
119 
120     }
121 
122     public void perform()
123         throws IOException, ComponentLookupException, InvalidVersionSpecificationException
124     {
125         // Files where local cache is (if any) and Lucene Index should be located
126         File centralLocalCache = new File( "target/central-cache" );
127         File centralIndexDir = new File( "target/central-index" );
128 
129         // Creators we want to use (search for fields it defines)
130         List<IndexCreator> indexers = new ArrayList<>();
131         indexers.add( plexusContainer.lookup( IndexCreator.class, "min" ) );
132         indexers.add( plexusContainer.lookup( IndexCreator.class, "jarContent" ) );
133         indexers.add( plexusContainer.lookup( IndexCreator.class, "maven-plugin" ) );
134 
135         // Create context for central repository index
136         centralContext =
137             indexer.createIndexingContext( "central-context", "central", centralLocalCache, centralIndexDir,
138                                            "https://repo1.maven.org/maven2", null, true, true, indexers );
139 
140         // Update the index (incremental update will happen if this is not 1st run and files are not deleted)
141         // This whole block below should not be executed on every app start, but rather controlled by some configuration
142         // since this block will always emit at least one HTTP GET. Central indexes are updated once a week, but
143         // other index sources might have different index publishing frequency.
144         // Preferred frequency is once a week.
145         if ( true )
146         {
147             Instant updateStart = Instant.now();
148             System.out.println( "Updating Index..." );
149             System.out.println( "This might take a while on first run, so please be patient!" );
150             // Create ResourceFetcher implementation to be used with IndexUpdateRequest
151             // Here, we use Wagon based one as shorthand, but all we need is a ResourceFetcher implementation
152             TransferListener listener = new AbstractTransferListener()
153             {
154                 Instant start;
155                 public void transferStarted( TransferEvent transferEvent )
156                 {
157                     start = Instant.now();
158                     System.out.print( "  Downloading " + transferEvent.getResource().getName() );
159                 }
160 
161                 public void transferProgress( TransferEvent transferEvent, byte[] buffer, int length )
162                 {
163                 }
164 
165                 public void transferCompleted( TransferEvent transferEvent )
166                 {
167                     System.out.println( " - Done in "
168                             + Duration.between( start, Instant.now() ).getSeconds() + " sec" );
169                 }
170             };
171             ResourceFetcher resourceFetcher = new WagonHelper.WagonFetcher( httpWagon, listener, null, null );
172 
173             Date centralContextCurrentTimestamp = centralContext.getTimestamp();
174             IndexUpdateRequest updateRequest = new IndexUpdateRequest( centralContext, resourceFetcher );
175             IndexUpdateResult updateResult = indexUpdater.fetchAndUpdateIndex( updateRequest );
176             if ( updateResult.isFullUpdate() )
177             {
178                 System.out.println( "Full update happened!" );
179             }
180             else if ( updateResult.getTimestamp().equals( centralContextCurrentTimestamp ) )
181             {
182                 System.out.println( "No update needed, index is up to date!" );
183             }
184             else
185             {
186                 System.out.println(
187                     "Incremental update happened, change covered " + centralContextCurrentTimestamp + " - "
188                         + updateResult.getTimestamp() + " period." );
189             }
190 
191             System.out.println( "Finished in " + Duration.between( updateStart, Instant.now() ).getSeconds() + " sec" );
192             System.out.println();
193         }
194 
195         System.out.println();
196         System.out.println( "Using index" );
197         System.out.println( "===========" );
198         System.out.println();
199 
200         // ====
201         // Case:
202         // dump all the GAVs
203         // NOTE: will not actually execute do this below, is too long to do (Central is HUGE), but is here as code
204         // example
205         if ( false )
206         {
207             final IndexSearcher searcher = centralContext.acquireIndexSearcher();
208             try
209             {
210                 final IndexReader ir = searcher.getIndexReader();
211                 Bits liveDocs = MultiBits.getLiveDocs( ir );
212                 for ( int i = 0; i < ir.maxDoc(); i++ )
213                 {
214                     if ( liveDocs == null || liveDocs.get( i ) )
215                     {
216                         final Document doc = ir.document( i );
217                         final ArtifactInfo ai = IndexUtils.constructArtifactInfo( doc, centralContext );
218                         System.out.println( ai.getGroupId() + ":" + ai.getArtifactId() + ":" + ai.getVersion() + ":"
219                                                 + ai.getClassifier() + " (sha1=" + ai.getSha1() + ")" );
220                     }
221                 }
222             }
223             finally
224             {
225                 centralContext.releaseIndexSearcher( searcher );
226             }
227         }
228 
229         // ====
230         // Case:
231         // Search for all GAVs with known G and A and having version greater than V
232 
233         final GenericVersionScheme versionScheme = new GenericVersionScheme();
234         final String versionString = "3.1.0";
235         final Version version = versionScheme.parseVersion( versionString );
236 
237         // construct the query for known GA
238         final Query groupIdQ =
239             indexer.constructQuery( MAVEN.GROUP_ID, new SourcedSearchExpression( "org.apache.maven" ) );
240         final Query artifactIdQ =
241             indexer.constructQuery( MAVEN.ARTIFACT_ID, new SourcedSearchExpression( "maven-plugin-api" ) );
242 
243         final BooleanQuery query = new BooleanQuery.Builder()
244             .add( groupIdQ, Occur.MUST )
245             .add( artifactIdQ, Occur.MUST )
246             // we want "jar" artifacts only
247             .add( indexer.constructQuery( MAVEN.PACKAGING, new SourcedSearchExpression( "jar" ) ), Occur.MUST )
248             // we want main artifacts only (no classifier)
249             // Note: this below is unfinished API, needs fixing
250             .add( indexer.constructQuery( MAVEN.CLASSIFIER,
251                     new SourcedSearchExpression( Field.NOT_PRESENT ) ), Occur.MUST_NOT )
252             .build();
253 
254         // construct the filter to express "V greater than"
255         final ArtifactInfoFilter versionFilter = ( ctx, ai ) ->
256         {
257             try
258             {
259                 final Version aiV = versionScheme.parseVersion( ai.getVersion() );
260                 // Use ">=" if you are INCLUSIVE
261                 return aiV.compareTo( version ) > 0;
262             }
263             catch ( InvalidVersionSpecificationException e )
264             {
265                 // do something here? be safe and include?
266                 return true;
267             }
268         };
269 
270         System.out.println(
271             "Searching for all GAVs with org.apache.maven:maven-plugin-api having V greater than 3.1.0" );
272         final IteratorSearchRequest request =
273             new IteratorSearchRequest( query, Collections.singletonList( centralContext ), versionFilter );
274         final IteratorSearchResponse response = indexer.searchIterator( request );
275         for ( ArtifactInfo ai : response )
276         {
277             System.out.println( ai.toString() );
278         }
279 
280         // Case:
281         // Use index
282         // Searching for some artifact
283         Query gidQ =
284             indexer.constructQuery( MAVEN.GROUP_ID, new SourcedSearchExpression( "org.apache.maven.indexer" ) );
285         Query aidQ = indexer.constructQuery( MAVEN.ARTIFACT_ID, new SourcedSearchExpression( "indexer-core" ) );
286 
287         BooleanQuery bq = new BooleanQuery.Builder()
288                 .add( gidQ, Occur.MUST )
289                 .add( aidQ, Occur.MUST )
290                 .build();
291 
292         searchAndDump( indexer, "all artifacts under GA org.apache.maven.indexer:indexer-core", bq );
293 
294         // Searching for some main artifact
295         bq = new BooleanQuery.Builder()
296                 .add( gidQ, Occur.MUST )
297                 .add( aidQ, Occur.MUST )
298                 .add( indexer.constructQuery( MAVEN.CLASSIFIER, new SourcedSearchExpression( "*" ) ), Occur.MUST_NOT )
299                 .build();
300 
301         searchAndDump( indexer, "main artifacts under GA org.apache.maven.indexer:indexer-core", bq );
302 
303         // doing sha1 search
304         searchAndDump( indexer, "SHA1 7ab67e6b20e5332a7fb4fdf2f019aec4275846c2",
305                        indexer.constructQuery( MAVEN.SHA1,
306                                                new SourcedSearchExpression( "7ab67e6b20e5332a7fb4fdf2f019aec4275846c2" )
307                        )
308         );
309 
310         searchAndDump( indexer, "SHA1 7ab67e6b20 (partial hash)",
311                        indexer.constructQuery( MAVEN.SHA1, new UserInputSearchExpression( "7ab67e6b20" ) ) );
312 
313         // doing classname search (incomplete classname)
314         searchAndDump( indexer, "classname DefaultNexusIndexer (note: Central does not publish classes in the index)",
315                        indexer.constructQuery( MAVEN.CLASSNAMES,
316                                                new UserInputSearchExpression( "DefaultNexusIndexer" ) ) );
317 
318         // doing search for all "canonical" maven plugins latest versions
319         bq = new BooleanQuery.Builder()
320             .add( indexer.constructQuery( MAVEN.PACKAGING, new SourcedSearchExpression( "maven-plugin" ) ), Occur.MUST )
321             .add( indexer.constructQuery( MAVEN.GROUP_ID,
322                     new SourcedSearchExpression( "org.apache.maven.plugins" ) ), Occur.MUST )
323             .build();
324 
325         searchGroupedAndDumpFlat( indexer, "all \"canonical\" maven plugins", bq, new GAGrouping() );
326 
327         // doing search for all archetypes latest versions
328         searchGroupedAndDump( indexer, "all maven archetypes (latest versions)",
329                               indexer.constructQuery( MAVEN.PACKAGING,
330                                                       new SourcedSearchExpression( "maven-archetype" ) ),
331                               new GAGrouping() );
332 
333         // close cleanly
334         indexer.closeIndexingContext( centralContext, false );
335     }
336 
337     public void searchAndDump( Indexer nexusIndexer, String descr, Query q )
338         throws IOException
339     {
340         System.out.println( "Searching for " + descr );
341 
342         FlatSearchResponse response = nexusIndexer.searchFlat( new FlatSearchRequest( q, centralContext ) );
343 
344         for ( ArtifactInfo ai : response.getResults() )
345         {
346             System.out.println( ai.toString() );
347         }
348 
349         System.out.println( "------" );
350         System.out.println( "Total: " + response.getTotalHitsCount() );
351         System.out.println();
352     }
353 
354     private static final int MAX_WIDTH = 60;
355 
356     public void searchGroupedAndDumpFlat( Indexer nexusIndexer, String descr, Query q, Grouping g )
357             throws IOException
358     {
359         System.out.println( "Searching for " + descr );
360 
361         GroupedSearchResponse response = nexusIndexer.searchGrouped( new GroupedSearchRequest( q, g, centralContext ) );
362 
363         for ( Map.Entry<String, ArtifactInfoGroup> entry : response.getResults().entrySet() )
364         {
365             ArtifactInfo ai = entry.getValue().getArtifactInfos().iterator().next();
366             System.out.println( "* " + ai.getGroupId() + ":" + ai.getArtifactId() + ":" + ai.getVersion() );
367         }
368 
369         System.out.println( "------" );
370         System.out.println( "Total record hits: " + response.getTotalHitsCount() );
371         System.out.println();
372     }
373 
374     public void searchGroupedAndDump( Indexer nexusIndexer, String descr, Query q, Grouping g )
375         throws IOException
376     {
377         System.out.println( "Searching for " + descr );
378 
379         GroupedSearchResponse response = nexusIndexer.searchGrouped( new GroupedSearchRequest( q, g, centralContext ) );
380 
381         for ( Map.Entry<String, ArtifactInfoGroup> entry : response.getResults().entrySet() )
382         {
383             ArtifactInfo ai = entry.getValue().getArtifactInfos().iterator().next();
384             System.out.println( "* Entry " + ai );
385             System.out.println( "  Latest version:  " + ai.getVersion() );
386             System.out.println( StringUtils.isBlank( ai.getDescription() )
387                                     ? "No description in plugin's POM."
388                                     : StringUtils.abbreviate( ai.getDescription(), MAX_WIDTH ) );
389             System.out.println();
390         }
391 
392         System.out.println( "------" );
393         System.out.println( "Total record hits: " + response.getTotalHitsCount() );
394         System.out.println();
395     }
396 }