Coverage Report - org.apache.maven.index.DefaultNexusIndexer
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultNexusIndexer
74 %
106/143
54 %
25/46
2,344
 
 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  
 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  
  * A default {@link NexusIndexer} implementation.
 56  
  * 
 57  
  * @author Tamas Cservenak
 58  
  * @author Eugene Kuleshov
 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  208
     {
 81  208
         this.indexingContexts = new ConcurrentHashMap<String, IndexingContext>();
 82  208
     }
 83  
 
 84  
     // ----------------------------------------------------------------------------
 85  
     // Contexts
 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  102
         IndexingContext context =
 94  
             new DefaultIndexingContext( id, repositoryId, repository, indexDirectory, repositoryUrl, indexUpdateUrl,
 95  
                 IndexCreatorSorter.sort( indexers ), false );
 96  
 
 97  102
         indexingContexts.put( context.getId(), context );
 98  
 
 99  102
         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  0
         IndexingContext context = null;
 108  
 
 109  
         try
 110  
         {
 111  0
             context =
 112  
                 new DefaultIndexingContext( id, repositoryId, repository, indexDirectory, repositoryUrl,
 113  
                     indexUpdateUrl, IndexCreatorSorter.sort( indexers ), true );
 114  
 
 115  0
             indexingContexts.put( context.getId(), context );
 116  
         }
 117  0
         catch ( UnsupportedExistingLuceneIndexException e )
 118  
         {
 119  
             // will not be thrown
 120  0
         }
 121  
 
 122  0
         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  149
         IndexingContext context =
 131  
             new DefaultIndexingContext( id, repositoryId, repository, directory, repositoryUrl, indexUpdateUrl,
 132  
                 IndexCreatorSorter.sort( indexers ), false );
 133  
 
 134  149
         indexingContexts.put( context.getId(), context );
 135  
 
 136  149
         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  10
         IndexingContext context = null;
 145  
 
 146  
         try
 147  
         {
 148  10
             context =
 149  
                 new DefaultIndexingContext( id, repositoryId, repository, directory, repositoryUrl, indexUpdateUrl,
 150  
                     IndexCreatorSorter.sort( indexers ), true );
 151  
 
 152  10
             indexingContexts.put( context.getId(), context );
 153  
         }
 154  0
         catch ( UnsupportedExistingLuceneIndexException e )
 155  
         {
 156  
             // will not be thrown
 157  10
         }
 158  
 
 159  10
         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  9
         IndexingContext context =
 168  
             new MergedIndexingContext( id, repositoryId, repository, indexDirectory, searchable,
 169  
                 new StaticContextMemberProvider( contexts ) );
 170  
 
 171  9
         indexingContexts.put( context.getId(), context );
 172  
 
 173  9
         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  0
         IndexingContext context =
 182  
             new MergedIndexingContext( id, repositoryId, repository, indexDirectory, searchable, membersProvider );
 183  
 
 184  0
         indexingContexts.put( context.getId(), context );
 185  
 
 186  0
         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  4
         IndexingContext context =
 195  
             new MergedIndexingContext( id, repositoryId, repository, indexDirectory, searchable,
 196  
                 new StaticContextMemberProvider( contexts ) );
 197  
 
 198  4
         indexingContexts.put( context.getId(), context );
 199  
 
 200  4
         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  0
         IndexingContext context =
 209  
             new MergedIndexingContext( id, repositoryId, repository, indexDirectory, searchable, membersProvider );
 210  
 
 211  0
         indexingContexts.put( context.getId(), context );
 212  
 
 213  0
         return context;
 214  
     }
 215  
 
 216  
     public void removeIndexingContext( IndexingContext context, boolean deleteFiles )
 217  
         throws IOException
 218  
     {
 219  437
         if ( indexingContexts.containsKey( context.getId() ) )
 220  
         {
 221  223
             indexingContexts.remove( context.getId() );
 222  223
             context.close( deleteFiles );
 223  
         }
 224  437
     }
 225  
 
 226  
     public Map<String, IndexingContext> getIndexingContexts()
 227  
     {
 228  18
         return Collections.unmodifiableMap( indexingContexts );
 229  
     }
 230  
 
 231  
     // ----------------------------------------------------------------------------
 232  
     // Scanning
 233  
     // ----------------------------------------------------------------------------
 234  
 
 235  
     public void scan( final IndexingContext context )
 236  
         throws IOException
 237  
     {
 238  164
         scan( context, null );
 239  164
     }
 240  
 
 241  
     public void scan( final IndexingContext context, boolean update )
 242  
         throws IOException
 243  
     {
 244  34
         scan( context, null, update );
 245  34
     }
 246  
 
 247  
     public void scan( final IndexingContext context, final ArtifactScanningListener listener )
 248  
         throws IOException
 249  
     {
 250  164
         scan( context, listener, false );
 251  164
     }
 252  
 
 253  
     public void scan( final IndexingContext context, final ArtifactScanningListener listener, final boolean update )
 254  
         throws IOException
 255  
     {
 256  206
         scan( context, null, listener, update );
 257  205
     }
 258  
 
 259  
     /**
 260  
      * Uses {@link Scanner} to scan repository content. A {@link ArtifactScanningListener} is used to process found
 261  
      * artifacts and to add them to the index using
 262  
      * {@link NexusIndexer#artifactDiscovered(ArtifactContext, IndexingContext)}.
 263  
      * 
 264  
      * @see DefaultScannerListener
 265  
      * @see #artifactDiscovered(ArtifactContext, IndexingContext)
 266  
      */
 267  
     public void scan( final IndexingContext context, final String fromPath, final ArtifactScanningListener listener,
 268  
                       final boolean update )
 269  
         throws IOException
 270  
     {
 271  215
         File repositoryDirectory = context.getRepository();
 272  
 
 273  215
         if ( repositoryDirectory == null )
 274  
         {
 275  
             // nothing to scan
 276  0
             return;
 277  
         }
 278  
 
 279  215
         if ( !repositoryDirectory.exists() )
 280  
         {
 281  1
             throw new IOException( "Repository directory " + repositoryDirectory + " does not exist" );
 282  
         }
 283  
 
 284  
         // always use temporary context when reindexing
 285  214
         File indexDir = context.getIndexDirectoryFile();
 286  214
         File dir = null;
 287  214
         if ( indexDir != null )
 288  
         {
 289  77
             dir = indexDir.getParentFile();
 290  
         }
 291  
 
 292  214
         File tmpFile = File.createTempFile( context.getId() + "-tmp", "" );
 293  214
         File tmpDir = new File( tmpFile.getParentFile(), tmpFile.getName() + ".dir" );
 294  214
         if ( !tmpDir.mkdirs() )
 295  
         {
 296  0
             throw new IOException( "Cannot create temporary directory: " + tmpDir );
 297  
         }
 298  
 
 299  214
         IndexingContext tmpContext = null;
 300  
         try
 301  
         {
 302  214
             FSDirectory directory = FSDirectory.open( tmpDir );
 303  
 
 304  214
             if ( update )
 305  
             {
 306  10
                 IndexUtils.copyDirectory( context.getIndexDirectory(), directory );
 307  
             }
 308  
 
 309  214
             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  214
             scanner.scan( new ScanningRequest( tmpContext, //
 319  
                 new DefaultScannerListener( tmpContext, indexerEngine, update, listener ), fromPath ) );
 320  
 
 321  214
             tmpContext.updateTimestamp( true );
 322  214
             context.replace( tmpContext.getIndexDirectory() );
 323  
 
 324  214
             removeIndexingContext( tmpContext, true );
 325  
         }
 326  0
         catch ( Exception ex )
 327  
         {
 328  0
             throw (IOException) new IOException( "Error scanning context " + context.getId() + ": " + ex ).initCause( ex );
 329  
         }
 330  
         finally
 331  
         {
 332  214
             if ( tmpContext != null )
 333  
             {
 334  214
                 tmpContext.close( true );
 335  
             }
 336  
 
 337  214
             if ( tmpFile.exists() )
 338  
             {
 339  214
                 tmpFile.delete();
 340  
             }
 341  
 
 342  214
             FileUtils.deleteDirectory( tmpDir );
 343  214
         }
 344  
 
 345  214
     }
 346  
 
 347  
     /**
 348  
      * Delegates to the {@link IndexerEngine} to add a new artifact to the index
 349  
      */
 350  
     public void artifactDiscovered( ArtifactContext ac, IndexingContext context )
 351  
         throws IOException
 352  
     {
 353  0
         if ( ac != null )
 354  
         {
 355  0
             indexerEngine.index( context, ac );
 356  
         }
 357  0
     }
 358  
 
 359  
     // ----------------------------------------------------------------------------
 360  
     // Modifying
 361  
     // ----------------------------------------------------------------------------
 362  
 
 363  
     /**
 364  
      * Delegates to the {@link IndexerEngine} to update artifact to the index
 365  
      */
 366  
     public void addArtifactToIndex( ArtifactContext ac, IndexingContext context )
 367  
         throws IOException
 368  
     {
 369  5586
         if ( ac != null )
 370  
         {
 371  5586
             indexerEngine.update( context, ac );
 372  
 
 373  5586
             context.commit();
 374  
         }
 375  5586
     }
 376  
 
 377  
     public void addArtifactsToIndex( Collection<ArtifactContext> ac, IndexingContext context )
 378  
         throws IOException
 379  
     {
 380  0
         if ( ac != null && !ac.isEmpty() )
 381  
         {
 382  0
             for ( ArtifactContext actx : ac )
 383  
             {
 384  0
                 indexerEngine.update( context, actx );
 385  
             }
 386  
 
 387  0
             context.commit();
 388  
         }
 389  0
     }
 390  
 
 391  
     /**
 392  
      * Delegates to the {@link IndexerEngine} to remove artifact from the index
 393  
      */
 394  
     public void deleteArtifactFromIndex( ArtifactContext ac, IndexingContext context )
 395  
         throws IOException
 396  
     {
 397  2
         if ( ac != null )
 398  
         {
 399  2
             indexerEngine.remove( context, ac );
 400  
 
 401  2
             context.commit();
 402  
         }
 403  2
     }
 404  
 
 405  
     public void deleteArtifactsFromIndex( Collection<ArtifactContext> ac, IndexingContext context )
 406  
         throws IOException
 407  
     {
 408  0
         if ( ac != null && !ac.isEmpty() )
 409  
         {
 410  0
             for ( ArtifactContext actx : ac )
 411  
             {
 412  0
                 indexerEngine.remove( context, actx );
 413  
 
 414  0
                 context.commit();
 415  
             }
 416  
         }
 417  0
     }
 418  
 
 419  
     // ----------------------------------------------------------------------------
 420  
     // Searching
 421  
     // ----------------------------------------------------------------------------
 422  
 
 423  
     public FlatSearchResponse searchFlat( FlatSearchRequest request )
 424  
         throws IOException
 425  
     {
 426  2445
         if ( request.getContexts().isEmpty() )
 427  
         {
 428  86
             return searcher.searchFlatPaged( request, indexingContexts.values() );
 429  
         }
 430  
         else
 431  
         {
 432  2359
             return searcher.forceSearchFlatPaged( request, request.getContexts() );
 433  
         }
 434  
     }
 435  
 
 436  
     public IteratorSearchResponse searchIterator( IteratorSearchRequest request )
 437  
         throws IOException
 438  
     {
 439  95
         if ( request.getContexts().isEmpty() )
 440  
         {
 441  8
             return searcher.searchIteratorPaged( request, indexingContexts.values() );
 442  
         }
 443  
         else
 444  
         {
 445  87
             return searcher.forceSearchIteratorPaged( request, request.getContexts() );
 446  
         }
 447  
     }
 448  
 
 449  
     public GroupedSearchResponse searchGrouped( GroupedSearchRequest request )
 450  
         throws IOException
 451  
     {
 452  63
         if ( request.getContexts().isEmpty() )
 453  
         {
 454  
             // search all
 455  63
             return searcher.searchGrouped( request, indexingContexts.values() );
 456  
         }
 457  
         else
 458  
         {
 459  
             // search targeted
 460  0
             return searcher.forceSearchGrouped( request, request.getContexts() );
 461  
         }
 462  
     }
 463  
 
 464  
     // ----------------------------------------------------------------------------
 465  
     // Query construction
 466  
     // ----------------------------------------------------------------------------
 467  
 
 468  
     @Deprecated
 469  
     public Query constructQuery( Field field, String query, SearchType type )
 470  
         throws IllegalArgumentException
 471  
     {
 472  
         try
 473  
         {
 474  188
             return queryCreator.constructQuery( field, query, type );
 475  
         }
 476  16
         catch ( ParseException e )
 477  
         {
 478  16
             throw new IllegalArgumentException( e );
 479  
         }
 480  
     }
 481  
 
 482  
     public Query constructQuery( Field field, SearchExpression expression )
 483  
         throws IllegalArgumentException
 484  
     {
 485  
         try
 486  
         {
 487  2364
             return queryCreator.constructQuery( field, expression );
 488  
         }
 489  0
         catch ( ParseException e )
 490  
         {
 491  0
             throw new IllegalArgumentException( e );
 492  
         }
 493  
     }
 494  
 
 495  
     // ----------------------------------------------------------------------------
 496  
     // Identification
 497  
     // ----------------------------------------------------------------------------
 498  
 
 499  
     public Collection<ArtifactInfo> identify( Field field, String query )
 500  
         throws IllegalArgumentException, IOException
 501  
     {
 502  8
         return identify( constructQuery( field, query, SearchType.EXACT ) );
 503  
     }
 504  
 
 505  
     public Collection<ArtifactInfo> identify( File artifact )
 506  
         throws IOException
 507  
     {
 508  10
         return identify( artifact, indexingContexts.values() );
 509  
     }
 510  
 
 511  
     public Collection<ArtifactInfo> identify( File artifact, Collection<IndexingContext> contexts )
 512  
         throws IOException
 513  
     {
 514  10
         FileInputStream is = null;
 515  
 
 516  
         try
 517  
         {
 518  10
             MessageDigest sha1 = MessageDigest.getInstance( "SHA-1" );
 519  
 
 520  10
             is = new FileInputStream( artifact );
 521  
 
 522  10
             byte[] buff = new byte[4096];
 523  
 
 524  
             int n;
 525  
 
 526  163
             while ( ( n = is.read( buff ) ) > -1 )
 527  
             {
 528  153
                 sha1.update( buff, 0, n );
 529  
             }
 530  
 
 531  10
             byte[] digest = sha1.digest();
 532  
 
 533  10
             Query q = constructQuery( MAVEN.SHA1, encode( digest ), SearchType.EXACT );
 534  
 
 535  10
             return identify( q, contexts );
 536  
         }
 537  0
         catch ( NoSuchAlgorithmException ex )
 538  
         {
 539  0
             IOException ioe = new IOException( "Unable to calculate digest" );
 540  0
             ioe.initCause( ex );
 541  0
             throw ioe;
 542  
         }
 543  
         finally
 544  
         {
 545  10
             IOUtil.close( is );
 546  
         }
 547  
     }
 548  
 
 549  
     public Collection<ArtifactInfo> identify( Query query )
 550  
         throws IOException
 551  
     {
 552  8
         return identify( query, indexingContexts.values() );
 553  
     }
 554  
 
 555  
     public Collection<ArtifactInfo> identify( Query query, Collection<IndexingContext> contexts )
 556  
         throws IOException
 557  
     {
 558  20
         IteratorSearchResponse result = searcher.searchIteratorPaged( new IteratorSearchRequest( query ), contexts );
 559  
 
 560  
         try
 561  
         {
 562  20
             ArrayList<ArtifactInfo> ais = new ArrayList<ArtifactInfo>( result.getTotalHitsCount() );
 563  
 
 564  20
             for ( ArtifactInfo ai : result )
 565  
             {
 566  18
                 ais.add( ai );
 567  
             }
 568  
 
 569  20
             return ais;
 570  
         }
 571  
         finally
 572  
         {
 573  20
             result.close();
 574  
         }
 575  
     }
 576  
 
 577  
     // ==
 578  
 
 579  1
     private static final char[] DIGITS = "0123456789abcdef".toCharArray();
 580  
 
 581  
     private static String encode( byte[] digest )
 582  
     {
 583  10
         char[] buff = new char[digest.length * 2];
 584  
 
 585  10
         int n = 0;
 586  
 
 587  210
         for ( byte b : digest )
 588  
         {
 589  200
             buff[n++] = DIGITS[( 0xF0 & b ) >> 4];
 590  200
             buff[n++] = DIGITS[0x0F & b];
 591  
         }
 592  
 
 593  10
         return new String( buff );
 594  
     }
 595  
 }