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.IOException;
23  import java.util.ArrayList;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Set;
27  
28  import org.apache.lucene.document.Document;
29  import org.apache.lucene.index.CorruptIndexException;
30  import org.apache.lucene.index.IndexReader;
31  import org.apache.lucene.index.MultiBits;
32  import org.apache.lucene.index.Term;
33  import org.apache.lucene.search.IndexSearcher;
34  import org.apache.lucene.search.TermQuery;
35  import org.apache.lucene.search.TopScoreDocCollector;
36  import org.apache.lucene.util.Bits;
37  import org.apache.maven.index.context.IndexingContext;
38  
39  /**
40   * A default scanning listener
41   * 
42   * @author Eugene Kuleshov
43   */
44  public class DefaultScannerListener
45      implements ArtifactScanningListener
46  {
47      private final IndexingContext context;
48  
49      private final IndexerEngine indexerEngine;
50  
51      private final boolean update;
52  
53      private final ArtifactScanningListener listener;
54  
55      private final Set<String> uinfos = new HashSet<>();
56  
57      private final Set<String> processedUinfos = new HashSet<>();
58  
59      private final Set<String> allGroups = new HashSet<>();
60  
61      private final Set<String> groups = new HashSet<>();
62  
63      private final List<Exception> exceptions = new ArrayList<>();
64  
65      private int count = 0;
66  
67      public DefaultScannerListener( IndexingContext context, //
68                              IndexerEngine indexerEngine, boolean update, //
69                              ArtifactScanningListener listener )
70      {
71          this.context = context;
72          this.indexerEngine = indexerEngine;
73          this.update = update;
74          this.listener = listener;
75      }
76  
77      public void scanningStarted( IndexingContext ctx )
78      {
79          try
80          {
81              if ( update )
82              {
83                  initialize( ctx );
84              }
85          }
86          catch ( IOException ex )
87          {
88              exceptions.add( ex );
89          }
90  
91          if ( listener != null )
92          {
93              listener.scanningStarted( ctx );
94          }
95      }
96  
97      public void artifactDiscovered( ArtifactContext ac )
98      {
99          String uinfo = ac.getArtifactInfo().getUinfo();
100 
101         // TODO: scattered across commented out changes while I was fixing NEXUS-2712, cstamas
102         // These changes should be applied by borks too much the fragile indexer
103 
104         // if ( VersionUtils.isSnapshot( ac.getArtifactInfo().version ) && processedUinfos.contains( uinfo ) )
105         if ( processedUinfos.contains( uinfo ) )
106         {
107             return; // skip individual snapshots
108         }
109 
110         boolean adding = processedUinfos.add( uinfo );
111 
112         if ( uinfos.contains( uinfo ) )
113         {
114             // already indexed
115             uinfos.remove( uinfo );
116             return;
117         }
118 
119         try
120         {
121             if ( listener != null )
122             {
123                 listener.artifactDiscovered( ac );
124             }
125 
126             if ( adding )
127             {
128                 indexerEngine.index( context, ac );
129             }
130             else
131             {
132                 indexerEngine.update( context, ac );
133             }
134 
135             for ( Exception e : ac.getErrors() )
136             {
137                 artifactError( ac, e );
138             }
139 
140             groups.add( ac.getArtifactInfo().getRootGroup() );
141             allGroups.add( ac.getArtifactInfo().getGroupId() );
142 
143             count++;
144         }
145         catch ( IOException ex )
146         {
147             artifactError( ac, ex );
148         }
149     }
150 
151     public void scanningFinished( IndexingContext ctx, ScanningResult result )
152     {
153         result.setTotalFiles( count );
154 
155         for ( Exception ex : exceptions )
156         {
157             result.addException( ex );
158         }
159 
160         try
161         {
162             context.optimize();
163 
164             context.setRootGroups( groups );
165 
166             context.setAllGroups( allGroups );
167 
168             if ( update && !context.isReceivingUpdates() )
169             {
170                 removeDeletedArtifacts( context, result, result.getRequest().getStartingPath() );
171             }
172         }
173         catch ( IOException ex )
174         {
175             result.addException( ex );
176         }
177 
178         if ( listener != null )
179         {
180             listener.scanningFinished( ctx, result );
181         }
182 
183         if ( result.getDeletedFiles() > 0 || result.getTotalFiles() > 0 )
184         {
185             try
186             {
187                 context.updateTimestamp( true );
188 
189                 context.optimize();
190             }
191             catch ( Exception ex )
192             {
193                 result.addException( ex );
194             }
195         }
196     }
197 
198     public void artifactError( ArtifactContext ac, Exception e )
199     {
200         exceptions.add( e );
201 
202         if ( listener != null )
203         {
204             listener.artifactError( ac, e );
205         }
206     }
207 
208     private void initialize( IndexingContext ctx )
209         throws IOException, CorruptIndexException
210     {
211         final IndexSearcher indexSearcher = ctx.acquireIndexSearcher();
212         try
213         {
214             final IndexReader r = indexSearcher.getIndexReader();
215             Bits liveDocs = MultiBits.getLiveDocs( r );
216 
217             for ( int i = 0; i < r.maxDoc(); i++ )
218             {
219                 if ( liveDocs == null || liveDocs.get( i ) )
220                 {
221                     Document d = r.document( i );
222 
223                     String uinfo = d.get( ArtifactInfo.UINFO );
224 
225                     if ( uinfo != null )
226                     {
227                         // if ctx is receiving updates (in other words, is a proxy),
228                         // there is no need to build a huge Set of strings with all uinfo's
229                         // as deletion detection in those cases have no effect. Also, the
230                         // removeDeletedArtifacts() method, that uses info gathered in this set
231                         // is invoked with same condition. As indexes of Central are getting huge,
232                         // the set grows enormously too, but is actually not used
233                         if ( !ctx.isReceivingUpdates() )
234                         {
235                             uinfos.add( uinfo );
236                         }
237 
238                         // add all existing groupIds to the lists, as they will
239                         // not be "discovered" and would be missing from the new list..
240                         String groupId = uinfo.substring( 0, uinfo.indexOf( '|' ) );
241                         int n = groupId.indexOf( '.' );
242                         groups.add( n == -1 ? groupId : groupId.substring( 0, n ) );
243                         allGroups.add( groupId );
244                     }
245                 }
246             }
247         }
248         finally
249         {
250             ctx.releaseIndexSearcher( indexSearcher );
251         }
252     }
253 
254     private void removeDeletedArtifacts( IndexingContext context, ScanningResult result, String contextPath )
255         throws IOException
256     {
257         int deleted = 0;
258 
259         final IndexSearcher indexSearcher = context.acquireIndexSearcher();
260         try
261         {
262             for ( String uinfo : uinfos )
263             {
264                 TopScoreDocCollector collector = TopScoreDocCollector.create( 1, Integer.MAX_VALUE );
265 
266                 indexSearcher.search( new TermQuery( new Term( ArtifactInfo.UINFO, uinfo ) ), collector );
267 
268                 if ( collector.getTotalHits() > 0 )
269                 {
270                     String[] ra = ArtifactInfo.FS_PATTERN.split( uinfo );
271 
272                     ArtifactInfo ai = new ArtifactInfo();
273 
274                     ai.setRepository( context.getRepositoryId() );
275 
276                     ai.setGroupId( ra[0] );
277 
278                     ai.setArtifactId( ra[1] );
279 
280                     ai.setVersion( ra[2] );
281 
282                     if ( ra.length > 3 )
283                     {
284                         ai.setClassifier( ArtifactInfo.renvl( ra[3] ) );
285                     }
286 
287                     if ( ra.length > 4 )
288                     {
289                         ai.setPackaging( ArtifactInfo.renvl( ra[4] ) );
290                     }
291 
292                     // minimal ArtifactContext for removal
293                     ArtifactContext ac = new ArtifactContext( null, null, null, ai, ai.calculateGav() );
294 
295                     for ( int i = 0; i < collector.getTotalHits(); i++ )
296                     {
297                         if ( contextPath == null
298                             || context.getGavCalculator().gavToPath( ac.getGav() ).startsWith( contextPath ) )
299                         {
300                             indexerEngine.remove( context, ac );
301                         }
302 
303                         deleted++;
304                     }
305                 }
306             }
307         }
308         finally
309         {
310             context.releaseIndexSearcher( indexSearcher );
311         }
312 
313         if ( deleted > 0 )
314         {
315             context.commit();
316         }
317 
318         result.setDeletedFiles( deleted );
319     }
320 
321 }