View Javadoc
1   package org.apache.maven.index;
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 javax.inject.Inject;
23  import javax.inject.Named;
24  import javax.inject.Singleton;
25  import java.io.File;
26  import java.io.FileInputStream;
27  import java.io.IOException;
28  import java.security.MessageDigest;
29  import java.security.NoSuchAlgorithmException;
30  import java.util.ArrayList;
31  import java.util.Collection;
32  import java.util.Collections;
33  import java.util.List;
34  import org.apache.lucene.queryparser.classic.ParseException;
35  import org.apache.lucene.search.Query;
36  import org.apache.maven.index.context.ContextMemberProvider;
37  import org.apache.maven.index.context.DefaultIndexingContext;
38  import org.apache.maven.index.context.ExistingLuceneIndexMismatchException;
39  import org.apache.maven.index.context.IndexCreator;
40  import org.apache.maven.index.context.IndexingContext;
41  import org.apache.maven.index.context.MergedIndexingContext;
42  import org.apache.maven.index.expr.SearchExpression;
43  import org.apache.maven.index.expr.SearchTypedStringSearchExpression;
44  import org.apache.maven.index.expr.SourcedSearchExpression;
45  import org.apache.maven.index.util.IndexCreatorSorter;
46  
47  /**
48   * A default {@link Indexer} implementation.
49   * 
50   * @author Tamas Cservenak
51   */
52  @Singleton
53  @Named
54  public class DefaultIndexer
55      implements Indexer
56  {
57  
58      private final SearchEngine searcher;
59  
60      private final IndexerEngine indexerEngine;
61  
62      private final QueryCreator queryCreator;
63  
64  
65      @Inject
66      public DefaultIndexer( SearchEngine searcher,
67                             IndexerEngine indexerEngine,
68                             QueryCreator queryCreator )
69      {
70          this.searcher = searcher;
71          this.indexerEngine = indexerEngine;
72          this.queryCreator = queryCreator;
73      }
74  
75      // ----------------------------------------------------------------------------
76      // Contexts
77      // ----------------------------------------------------------------------------
78  
79      public IndexingContext createIndexingContext( String id, String repositoryId, File repository, File indexDirectory,
80                                                    String repositoryUrl, String indexUpdateUrl, boolean searchable,
81                                                    boolean reclaim, List<? extends IndexCreator> indexers )
82          throws IOException, ExistingLuceneIndexMismatchException, IllegalArgumentException
83      {
84          final IndexingContext context =
85              new DefaultIndexingContext( id, repositoryId, repository, indexDirectory, repositoryUrl, indexUpdateUrl,
86                  IndexCreatorSorter.sort( indexers ), reclaim );
87          context.setSearchable( searchable );
88          return context;
89      }
90  
91      public IndexingContext createMergedIndexingContext( String id, String repositoryId, File repository,
92                                                          File indexDirectory, boolean searchable,
93                                                          ContextMemberProvider membersProvider )
94          throws IOException
95      {
96          IndexingContext context =
97              new MergedIndexingContext( id, repositoryId, repository, indexDirectory, searchable, membersProvider );
98          return context;
99      }
100 
101     public void closeIndexingContext( IndexingContext context, boolean deleteFiles )
102         throws IOException
103     {
104         context.close( deleteFiles );
105     }
106 
107     // ----------------------------------------------------------------------------
108     // Modifying
109     // ----------------------------------------------------------------------------
110 
111     public void addArtifactToIndex( ArtifactContext ac, IndexingContext context )
112         throws IOException
113     {
114         if ( ac != null )
115         {
116             indexerEngine.update( context, ac );
117 
118             context.commit();
119         }
120     }
121 
122     public void addArtifactsToIndex( Collection<ArtifactContext> ac, IndexingContext context )
123         throws IOException
124     {
125         if ( ac != null && !ac.isEmpty() )
126         {
127             for ( ArtifactContext actx : ac )
128             {
129                 indexerEngine.update( context, actx );
130             }
131 
132             context.commit();
133         }
134     }
135 
136     public void deleteArtifactsFromIndex( Collection<ArtifactContext> ac, IndexingContext context )
137         throws IOException
138     {
139         if ( ac != null && !ac.isEmpty() )
140         {
141             for ( ArtifactContext actx : ac )
142             {
143                 indexerEngine.remove( context, actx );
144 
145                 context.commit();
146             }
147         }
148     }
149 
150     // ----------------------------------------------------------------------------
151     // Searching
152     // ----------------------------------------------------------------------------
153 
154     public FlatSearchResponse searchFlat( FlatSearchRequest request )
155         throws IOException
156     {
157         if ( request.getContexts().isEmpty() )
158         {
159             return new FlatSearchResponse( request.getQuery(), 0, Collections.<ArtifactInfo>emptySet() );
160         }
161         else
162         {
163             return searcher.forceSearchFlatPaged( request, request.getContexts() );
164         }
165     }
166 
167     public IteratorSearchResponse searchIterator( IteratorSearchRequest request )
168         throws IOException
169     {
170         if ( request.getContexts().isEmpty() )
171         {
172             return IteratorSearchResponse.empty( request.getQuery() );
173         }
174         else
175         {
176             return searcher.forceSearchIteratorPaged( request, request.getContexts() );
177         }
178     }
179 
180     public GroupedSearchResponse searchGrouped( GroupedSearchRequest request )
181         throws IOException
182     {
183         if ( request.getContexts().isEmpty() )
184         {
185             return new GroupedSearchResponse( request.getQuery(), 0,
186                                               Collections.<String, ArtifactInfoGroup>emptyMap() );
187         }
188         else
189         {
190             // search targeted
191             return searcher.forceSearchGrouped( request, request.getContexts() );
192         }
193     }
194 
195     // ----------------------------------------------------------------------------
196     // Identification
197     // ----------------------------------------------------------------------------
198 
199     public Collection<ArtifactInfo> identify( final File artifact, final Collection<IndexingContext> contexts )
200         throws IOException
201     {
202         try ( FileInputStream is = new FileInputStream( artifact ) )
203         {
204             final MessageDigest sha1 = MessageDigest.getInstance( "SHA-1" );
205             final byte[] buff = new byte[4096];
206             int n;
207             while ( ( n = is.read( buff ) ) > -1 )
208             {
209                 sha1.update( buff, 0, n );
210             }
211             byte[] digest = sha1.digest();
212             return identify( constructQuery( MAVEN.SHA1, new SourcedSearchExpression( encode( digest ) ) ), contexts );
213         }
214         catch ( NoSuchAlgorithmException ex )
215         {
216             IOException ioe = new IOException( "Unable to calculate digest" );
217             ioe.initCause( ex );
218             throw ioe;
219         }
220     }
221 
222     public Collection<ArtifactInfo> identify( Query query, Collection<IndexingContext> contexts )
223         throws IOException
224     {
225         final IteratorSearchResponse result =
226             searcher.searchIteratorPaged( new IteratorSearchRequest( query ), contexts );
227         try
228         {
229             final ArrayList<ArtifactInfo> ais = new ArrayList<ArtifactInfo>( result.getTotalHitsCount() );
230             for ( ArtifactInfo ai : result )
231             {
232                 ais.add( ai );
233             }
234             return ais;
235         }
236         finally
237         {
238             result.close();
239         }
240     }
241 
242     // ----------------------------------------------------------------------------
243     // Query construction
244     // ----------------------------------------------------------------------------
245 
246     public Query constructQuery( Field field, SearchExpression expression )
247         throws IllegalArgumentException
248     {
249         try
250         {
251             return queryCreator.constructQuery( field, expression );
252         }
253         catch ( ParseException e )
254         {
255             throw new IllegalArgumentException( e );
256         }
257     }
258 
259     public Query constructQuery( Field field, String expression, SearchType searchType )
260         throws IllegalArgumentException
261     {
262         return constructQuery( field, new SearchTypedStringSearchExpression( expression, searchType ) );
263     }
264     // ==
265 
266     private static final char[] DIGITS = "0123456789abcdef".toCharArray();
267 
268     private static String encode( byte[] digest )
269     {
270         char[] buff = new char[digest.length * 2];
271 
272         int n = 0;
273 
274         for ( byte b : digest )
275         {
276             buff[n++] = DIGITS[( 0xF0 & b ) >> 4];
277             buff[n++] = DIGITS[0x0F & b];
278         }
279 
280         return new String( buff );
281     }
282 }