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