1 package org.apache.maven.index;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 import java.util.Map;
32 import java.util.concurrent.ConcurrentHashMap;
33
34 import org.apache.lucene.queryParser.ParseException;
35 import org.apache.lucene.search.Query;
36 import org.apache.lucene.store.Directory;
37 import org.apache.lucene.store.FSDirectory;
38 import org.apache.maven.index.context.ContextMemberProvider;
39 import org.apache.maven.index.context.DefaultIndexingContext;
40 import org.apache.maven.index.context.IndexCreator;
41 import org.apache.maven.index.context.IndexUtils;
42 import org.apache.maven.index.context.IndexingContext;
43 import org.apache.maven.index.context.MergedIndexingContext;
44 import org.apache.maven.index.context.StaticContextMemberProvider;
45 import org.apache.maven.index.context.UnsupportedExistingLuceneIndexException;
46 import org.apache.maven.index.expr.SearchExpression;
47 import org.apache.maven.index.util.IndexCreatorSorter;
48 import org.codehaus.plexus.component.annotations.Component;
49 import org.codehaus.plexus.component.annotations.Requirement;
50 import org.codehaus.plexus.logging.AbstractLogEnabled;
51 import org.codehaus.plexus.util.FileUtils;
52 import org.codehaus.plexus.util.IOUtil;
53
54
55
56
57
58
59
60 @Component( role = NexusIndexer.class )
61 public class DefaultNexusIndexer
62 extends AbstractLogEnabled
63 implements NexusIndexer
64 {
65 @Requirement
66 private Scanner scanner;
67
68 @Requirement
69 private SearchEngine searcher;
70
71 @Requirement
72 private IndexerEngine indexerEngine;
73
74 @Requirement
75 private QueryCreator queryCreator;
76
77 private Map<String, IndexingContext> indexingContexts;
78
79 public DefaultNexusIndexer()
80 {
81 this.indexingContexts = new ConcurrentHashMap<String, IndexingContext>();
82 }
83
84
85
86
87
88 public IndexingContext addIndexingContext( String id, String repositoryId, File repository, File indexDirectory,
89 String repositoryUrl, String indexUpdateUrl,
90 List<? extends IndexCreator> indexers )
91 throws IOException, UnsupportedExistingLuceneIndexException
92 {
93 IndexingContext context =
94 new DefaultIndexingContext( id, repositoryId, repository, indexDirectory, repositoryUrl, indexUpdateUrl,
95 IndexCreatorSorter.sort( indexers ), false );
96
97 indexingContexts.put( context.getId(), context );
98
99 return context;
100 }
101
102 public IndexingContext addIndexingContextForced( String id, String repositoryId, File repository,
103 File indexDirectory, String repositoryUrl, String indexUpdateUrl,
104 List<? extends IndexCreator> indexers )
105 throws IOException
106 {
107 IndexingContext context = null;
108
109 try
110 {
111 context =
112 new DefaultIndexingContext( id, repositoryId, repository, indexDirectory, repositoryUrl,
113 indexUpdateUrl, IndexCreatorSorter.sort( indexers ), true );
114
115 indexingContexts.put( context.getId(), context );
116 }
117 catch ( UnsupportedExistingLuceneIndexException e )
118 {
119
120 }
121
122 return context;
123 }
124
125 public IndexingContext addIndexingContext( String id, String repositoryId, File repository, Directory directory,
126 String repositoryUrl, String indexUpdateUrl,
127 List<? extends IndexCreator> indexers )
128 throws IOException, UnsupportedExistingLuceneIndexException
129 {
130 IndexingContext context =
131 new DefaultIndexingContext( id, repositoryId, repository, directory, repositoryUrl, indexUpdateUrl,
132 IndexCreatorSorter.sort( indexers ), false );
133
134 indexingContexts.put( context.getId(), context );
135
136 return context;
137 }
138
139 public IndexingContext addIndexingContextForced( String id, String repositoryId, File repository,
140 Directory directory, String repositoryUrl, String indexUpdateUrl,
141 List<? extends IndexCreator> indexers )
142 throws IOException
143 {
144 IndexingContext context = null;
145
146 try
147 {
148 context =
149 new DefaultIndexingContext( id, repositoryId, repository, directory, repositoryUrl, indexUpdateUrl,
150 IndexCreatorSorter.sort( indexers ), true );
151
152 indexingContexts.put( context.getId(), context );
153 }
154 catch ( UnsupportedExistingLuceneIndexException e )
155 {
156
157 }
158
159 return context;
160 }
161
162 public IndexingContext addMergedIndexingContext( String id, String repositoryId, File repository,
163 File indexDirectory, boolean searchable,
164 Collection<IndexingContext> contexts )
165 throws IOException
166 {
167 IndexingContext context =
168 new MergedIndexingContext( id, repositoryId, repository, indexDirectory, searchable,
169 new StaticContextMemberProvider( contexts ) );
170
171 indexingContexts.put( context.getId(), context );
172
173 return context;
174 }
175
176 public IndexingContext addMergedIndexingContext( String id, String repositoryId, File repository,
177 File indexDirectory, boolean searchable,
178 ContextMemberProvider membersProvider )
179 throws IOException
180 {
181 IndexingContext context =
182 new MergedIndexingContext( id, repositoryId, repository, indexDirectory, searchable, membersProvider );
183
184 indexingContexts.put( context.getId(), context );
185
186 return context;
187 }
188
189 public IndexingContext addMergedIndexingContext( String id, String repositoryId, File repository,
190 Directory indexDirectory, boolean searchable,
191 Collection<IndexingContext> contexts )
192 throws IOException
193 {
194 IndexingContext context =
195 new MergedIndexingContext( id, repositoryId, repository, indexDirectory, searchable,
196 new StaticContextMemberProvider( contexts ) );
197
198 indexingContexts.put( context.getId(), context );
199
200 return context;
201 }
202
203 public IndexingContext addMergedIndexingContext( String id, String repositoryId, File repository,
204 Directory indexDirectory, boolean searchable,
205 ContextMemberProvider membersProvider )
206 throws IOException
207 {
208 IndexingContext context =
209 new MergedIndexingContext( id, repositoryId, repository, indexDirectory, searchable, membersProvider );
210
211 indexingContexts.put( context.getId(), context );
212
213 return context;
214 }
215
216 public void removeIndexingContext( IndexingContext context, boolean deleteFiles )
217 throws IOException
218 {
219 if ( indexingContexts.containsKey( context.getId() ) )
220 {
221 indexingContexts.remove( context.getId() );
222 context.close( deleteFiles );
223 }
224 }
225
226 public Map<String, IndexingContext> getIndexingContexts()
227 {
228 return Collections.unmodifiableMap( indexingContexts );
229 }
230
231
232
233
234
235 public void scan( final IndexingContext context )
236 throws IOException
237 {
238 scan( context, null );
239 }
240
241 public void scan( final IndexingContext context, boolean update )
242 throws IOException
243 {
244 scan( context, null, update );
245 }
246
247 public void scan( final IndexingContext context, final ArtifactScanningListener listener )
248 throws IOException
249 {
250 scan( context, listener, false );
251 }
252
253 public void scan( final IndexingContext context, final ArtifactScanningListener listener, final boolean update )
254 throws IOException
255 {
256 scan( context, null, listener, update );
257 }
258
259
260
261
262
263
264
265
266
267 public void scan( final IndexingContext context, final String fromPath, final ArtifactScanningListener listener,
268 final boolean update )
269 throws IOException
270 {
271 File repositoryDirectory = context.getRepository();
272
273 if ( repositoryDirectory == null )
274 {
275
276 return;
277 }
278
279 if ( !repositoryDirectory.exists() )
280 {
281 throw new IOException( "Repository directory " + repositoryDirectory + " does not exist" );
282 }
283
284
285 File indexDir = context.getIndexDirectoryFile();
286 File dir = null;
287 if ( indexDir != null )
288 {
289 dir = indexDir.getParentFile();
290 }
291
292 File tmpFile = File.createTempFile( context.getId() + "-tmp", "" );
293 File tmpDir = new File( tmpFile.getParentFile(), tmpFile.getName() + ".dir" );
294 if ( !tmpDir.mkdirs() )
295 {
296 throw new IOException( "Cannot create temporary directory: " + tmpDir );
297 }
298
299 IndexingContext tmpContext = null;
300 try
301 {
302 FSDirectory directory = FSDirectory.open( tmpDir );
303
304 if ( update )
305 {
306 IndexUtils.copyDirectory( context.getIndexDirectory(), directory );
307 }
308
309 tmpContext = new DefaultIndexingContext( context.getId() + "-tmp",
310 context.getRepositoryId(),
311 context.getRepository(),
312 directory,
313 context.getRepositoryUrl(),
314 context.getIndexUpdateUrl(),
315 context.getIndexCreators(),
316 true );
317
318 scanner.scan( new ScanningRequest( tmpContext,
319 new DefaultScannerListener( tmpContext, indexerEngine, update, listener ), fromPath ) );
320
321 tmpContext.updateTimestamp( true );
322 context.replace( tmpContext.getIndexDirectory() );
323
324 removeIndexingContext( tmpContext, true );
325 }
326 catch ( Exception ex )
327 {
328 throw (IOException) new IOException( "Error scanning context " + context.getId() + ": " + ex ).initCause( ex );
329 }
330 finally
331 {
332 if ( tmpContext != null )
333 {
334 tmpContext.close( true );
335 }
336
337 if ( tmpFile.exists() )
338 {
339 tmpFile.delete();
340 }
341
342 FileUtils.deleteDirectory( tmpDir );
343 }
344
345 }
346
347
348
349
350 public void artifactDiscovered( ArtifactContext ac, IndexingContext context )
351 throws IOException
352 {
353 if ( ac != null )
354 {
355 indexerEngine.index( context, ac );
356 }
357 }
358
359
360
361
362
363
364
365
366 public void addArtifactToIndex( ArtifactContext ac, IndexingContext context )
367 throws IOException
368 {
369 if ( ac != null )
370 {
371 indexerEngine.update( context, ac );
372
373 context.commit();
374 }
375 }
376
377 public void addArtifactsToIndex( Collection<ArtifactContext> ac, IndexingContext context )
378 throws IOException
379 {
380 if ( ac != null && !ac.isEmpty() )
381 {
382 for ( ArtifactContext actx : ac )
383 {
384 indexerEngine.update( context, actx );
385 }
386
387 context.commit();
388 }
389 }
390
391
392
393
394 public void deleteArtifactFromIndex( ArtifactContext ac, IndexingContext context )
395 throws IOException
396 {
397 if ( ac != null )
398 {
399 indexerEngine.remove( context, ac );
400
401 context.commit();
402 }
403 }
404
405 public void deleteArtifactsFromIndex( Collection<ArtifactContext> ac, IndexingContext context )
406 throws IOException
407 {
408 if ( ac != null && !ac.isEmpty() )
409 {
410 for ( ArtifactContext actx : ac )
411 {
412 indexerEngine.remove( context, actx );
413
414 context.commit();
415 }
416 }
417 }
418
419
420
421
422
423 public FlatSearchResponse searchFlat( FlatSearchRequest request )
424 throws IOException
425 {
426 if ( request.getContexts().isEmpty() )
427 {
428 return searcher.searchFlatPaged( request, indexingContexts.values() );
429 }
430 else
431 {
432 return searcher.forceSearchFlatPaged( request, request.getContexts() );
433 }
434 }
435
436 public IteratorSearchResponse searchIterator( IteratorSearchRequest request )
437 throws IOException
438 {
439 if ( request.getContexts().isEmpty() )
440 {
441 return searcher.searchIteratorPaged( request, indexingContexts.values() );
442 }
443 else
444 {
445 return searcher.forceSearchIteratorPaged( request, request.getContexts() );
446 }
447 }
448
449 public GroupedSearchResponse searchGrouped( GroupedSearchRequest request )
450 throws IOException
451 {
452 if ( request.getContexts().isEmpty() )
453 {
454
455 return searcher.searchGrouped( request, indexingContexts.values() );
456 }
457 else
458 {
459
460 return searcher.forceSearchGrouped( request, request.getContexts() );
461 }
462 }
463
464
465
466
467
468 @Deprecated
469 public Query constructQuery( Field field, String query, SearchType type )
470 throws IllegalArgumentException
471 {
472 try
473 {
474 return queryCreator.constructQuery( field, query, type );
475 }
476 catch ( ParseException e )
477 {
478 throw new IllegalArgumentException( e );
479 }
480 }
481
482 public Query constructQuery( Field field, SearchExpression expression )
483 throws IllegalArgumentException
484 {
485 try
486 {
487 return queryCreator.constructQuery( field, expression );
488 }
489 catch ( ParseException e )
490 {
491 throw new IllegalArgumentException( e );
492 }
493 }
494
495
496
497
498
499 public Collection<ArtifactInfo> identify( Field field, String query )
500 throws IllegalArgumentException, IOException
501 {
502 return identify( constructQuery( field, query, SearchType.EXACT ) );
503 }
504
505 public Collection<ArtifactInfo> identify( File artifact )
506 throws IOException
507 {
508 return identify( artifact, indexingContexts.values() );
509 }
510
511 public Collection<ArtifactInfo> identify( File artifact, Collection<IndexingContext> contexts )
512 throws IOException
513 {
514 FileInputStream is = null;
515
516 try
517 {
518 MessageDigest sha1 = MessageDigest.getInstance( "SHA-1" );
519
520 is = new FileInputStream( artifact );
521
522 byte[] buff = new byte[4096];
523
524 int n;
525
526 while ( ( n = is.read( buff ) ) > -1 )
527 {
528 sha1.update( buff, 0, n );
529 }
530
531 byte[] digest = sha1.digest();
532
533 Query q = constructQuery( MAVEN.SHA1, encode( digest ), SearchType.EXACT );
534
535 return identify( q, contexts );
536 }
537 catch ( NoSuchAlgorithmException ex )
538 {
539 IOException ioe = new IOException( "Unable to calculate digest" );
540 ioe.initCause( ex );
541 throw ioe;
542 }
543 finally
544 {
545 IOUtil.close( is );
546 }
547 }
548
549 public Collection<ArtifactInfo> identify( Query query )
550 throws IOException
551 {
552 return identify( query, indexingContexts.values() );
553 }
554
555 public Collection<ArtifactInfo> identify( Query query, Collection<IndexingContext> contexts )
556 throws IOException
557 {
558 IteratorSearchResponse result = searcher.searchIteratorPaged( new IteratorSearchRequest( query ), contexts );
559
560 try
561 {
562 ArrayList<ArtifactInfo> ais = new ArrayList<ArtifactInfo>( result.getTotalHitsCount() );
563
564 for ( ArtifactInfo ai : result )
565 {
566 ais.add( ai );
567 }
568
569 return ais;
570 }
571 finally
572 {
573 result.close();
574 }
575 }
576
577
578
579 private static final char[] DIGITS = "0123456789abcdef".toCharArray();
580
581 private static String encode( byte[] digest )
582 {
583 char[] buff = new char[digest.length * 2];
584
585 int n = 0;
586
587 for ( byte b : digest )
588 {
589 buff[n++] = DIGITS[( 0xF0 & b ) >> 4];
590 buff[n++] = DIGITS[0x0F & b];
591 }
592
593 return new String( buff );
594 }
595 }