Coverage Report - org.apache.maven.index.context.DefaultIndexingContext
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultIndexingContext
87 %
325/373
80 %
93/116
2,576
DefaultIndexingContext$1
80 %
8/10
100 %
4/4
2,576
 
 1  
 package org.apache.maven.index.context;
 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.IOException;
 24  
 import java.util.Arrays;
 25  
 import java.util.Collection;
 26  
 import java.util.Collections;
 27  
 import java.util.Date;
 28  
 import java.util.LinkedHashSet;
 29  
 import java.util.List;
 30  
 import java.util.Set;
 31  
 import java.util.concurrent.locks.ReadWriteLock;
 32  
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 33  
 
 34  
 import org.apache.lucene.analysis.Analyzer;
 35  
 import org.apache.lucene.document.Document;
 36  
 import org.apache.lucene.document.Field;
 37  
 import org.apache.lucene.index.CorruptIndexException;
 38  
 import org.apache.lucene.index.IndexFileNameFilter;
 39  
 import org.apache.lucene.index.IndexReader;
 40  
 import org.apache.lucene.index.IndexWriter;
 41  
 import org.apache.lucene.index.SerialMergeScheduler;
 42  
 import org.apache.lucene.index.Term;
 43  
 import org.apache.lucene.search.IndexSearcher;
 44  
 import org.apache.lucene.search.TermQuery;
 45  
 import org.apache.lucene.search.TopDocs;
 46  
 import org.apache.lucene.search.TopScoreDocCollector;
 47  
 import org.apache.lucene.store.Directory;
 48  
 import org.apache.lucene.store.FSDirectory;
 49  
 import org.apache.maven.index.ArtifactInfo;
 50  
 import org.apache.maven.index.artifact.GavCalculator;
 51  
 import org.apache.maven.index.artifact.M2GavCalculator;
 52  
 import org.codehaus.plexus.util.StringUtils;
 53  
 
 54  
 /**
 55  
  * The default {@link IndexingContext} implementation.
 56  
  * 
 57  
  * @author Jason van Zyl
 58  
  * @author Tamas Cservenak
 59  
  */
 60  410
 public class DefaultIndexingContext
 61  
     extends AbstractIndexingContext
 62  
 {
 63  
     /**
 64  
      * A standard location for indices served up by a webserver.
 65  
      */
 66  
     private static final String INDEX_DIRECTORY = ".index";
 67  
 
 68  
     public static final String FLD_DESCRIPTOR = "DESCRIPTOR";
 69  
 
 70  
     private static final String FLD_DESCRIPTOR_CONTENTS = "NexusIndex";
 71  
 
 72  
     private static final String FLD_IDXINFO = "IDXINFO";
 73  
 
 74  
     private static final String VERSION = "1.0";
 75  
 
 76  1
     private static final Term DESCRIPTOR_TERM = new Term( FLD_DESCRIPTOR, FLD_DESCRIPTOR_CONTENTS );
 77  
 
 78  
     private Directory indexDirectory;
 79  
 
 80  
     private File indexDirectoryFile;
 81  
 
 82  
     private String id;
 83  
 
 84  
     private boolean searchable;
 85  
 
 86  
     private String repositoryId;
 87  
 
 88  
     private File repository;
 89  
 
 90  
     private String repositoryUrl;
 91  
 
 92  
     private String indexUpdateUrl;
 93  
 
 94  
     private IndexReader indexReader;
 95  
 
 96  
     private NexusIndexSearcher indexSearcher;
 97  
 
 98  
     // disabled for now, see getReadOnlyIndexSearcher() method for explanation
 99  
     // private NexusIndexSearcher readOnlyIndexSearcher;
 100  
 
 101  
     private NexusIndexWriter indexWriter;
 102  
 
 103  
     private Date timestamp;
 104  
 
 105  
     private List<? extends IndexCreator> indexCreators;
 106  
 
 107  
     /**
 108  
      * Currently nexus-indexer knows only M2 reposes
 109  
      * <p>
 110  
      * XXX move this into a concrete Scanner implementation
 111  
      */
 112  
     private GavCalculator gavCalculator;
 113  
 
 114  475
     private ReadWriteLock indexMaintenanceLock = new ReentrantReadWriteLock();
 115  
 
 116  
     private Thread bottleWarmerThread;
 117  
 
 118  
     private DefaultIndexingContext( String id,
 119  
                                     String repositoryId,
 120  
                                     File repository, //
 121  
                                     String repositoryUrl, String indexUpdateUrl,
 122  
                                     List<? extends IndexCreator> indexCreators, Directory indexDirectory,
 123  
                                     boolean reclaimIndex )
 124  
         throws UnsupportedExistingLuceneIndexException, IOException
 125  475
     {
 126  475
         this.id = id;
 127  
 
 128  475
         this.searchable = true;
 129  
 
 130  475
         this.repositoryId = repositoryId;
 131  
 
 132  475
         this.repository = repository;
 133  
 
 134  475
         this.repositoryUrl = repositoryUrl;
 135  
 
 136  475
         this.indexUpdateUrl = indexUpdateUrl;
 137  
 
 138  475
         this.indexReader = null;
 139  
 
 140  475
         this.indexWriter = null;
 141  
 
 142  475
         this.indexCreators = indexCreators;
 143  
 
 144  475
         this.indexDirectory = indexDirectory;
 145  
 
 146  
         // eh?
 147  
         // Guice does NOT initialize these, and we have to do manually?
 148  
         // While in Plexus, all is well, but when in guice-shim,
 149  
         // these objects are still LazyHintedBeans or what not and IndexerFields are NOT registered!
 150  475
         for ( IndexCreator indexCreator : indexCreators )
 151  
         {
 152  1300
             indexCreator.getIndexerFields();
 153  
         }
 154  
 
 155  475
         this.gavCalculator = new M2GavCalculator();
 156  
 
 157  475
         prepareIndex( reclaimIndex );
 158  
 
 159  475
         installBottleWarmer();
 160  475
     }
 161  
 
 162  
     public DefaultIndexingContext( String id, String repositoryId, File repository, File indexDirectoryFile,
 163  
                                    String repositoryUrl, String indexUpdateUrl,
 164  
                                    List<? extends IndexCreator> indexCreators, boolean reclaimIndex )
 165  
         throws IOException, UnsupportedExistingLuceneIndexException
 166  
     {
 167  102
         this( id, repositoryId, repository, repositoryUrl, indexUpdateUrl, indexCreators,
 168  
             FSDirectory.open( indexDirectoryFile ), reclaimIndex );
 169  
 
 170  102
         this.indexDirectoryFile = indexDirectoryFile;
 171  102
     }
 172  
 
 173  
     public DefaultIndexingContext( String id, String repositoryId, File repository, Directory indexDirectory,
 174  
                                    String repositoryUrl, String indexUpdateUrl,
 175  
                                    List<? extends IndexCreator> indexCreators, boolean reclaimIndex )
 176  
         throws IOException, UnsupportedExistingLuceneIndexException
 177  
     {
 178  373
         this( id, repositoryId, repository, repositoryUrl, indexUpdateUrl, indexCreators, indexDirectory, reclaimIndex );
 179  
 
 180  373
         if ( indexDirectory instanceof FSDirectory )
 181  
         {
 182  222
             this.indexDirectoryFile = ( (FSDirectory) indexDirectory ).getFile();
 183  
         }
 184  373
     }
 185  
 
 186  
     public void lock()
 187  
     {
 188  416343
         indexMaintenanceLock.readLock().lock();
 189  416343
     }
 190  
 
 191  
     public void unlock()
 192  
     {
 193  416343
         indexMaintenanceLock.readLock().unlock();
 194  416343
     }
 195  
 
 196  
     public void lockExclusively()
 197  
     {
 198  14031
         indexMaintenanceLock.writeLock().lock();
 199  14031
     }
 200  
 
 201  
     public void unlockExclusively()
 202  
     {
 203  14031
         indexMaintenanceLock.writeLock().unlock();
 204  14031
     }
 205  
 
 206  
     public Directory getIndexDirectory()
 207  
     {
 208  1007
         return indexDirectory;
 209  
     }
 210  
 
 211  
     public File getIndexDirectoryFile()
 212  
     {
 213  293
         return indexDirectoryFile;
 214  
     }
 215  
 
 216  
     private void prepareIndex( boolean reclaimIndex )
 217  
         throws IOException, UnsupportedExistingLuceneIndexException
 218  
     {
 219  480
         if ( IndexReader.indexExists( indexDirectory ) )
 220  
         {
 221  
             try
 222  
             {
 223  
                 // unlock the dir forcibly
 224  27
                 if ( IndexWriter.isLocked( indexDirectory ) )
 225  
                 {
 226  5
                     IndexWriter.unlock( indexDirectory );
 227  
                 }
 228  
 
 229  27
                 openAndWarmup();
 230  
 
 231  27
                 checkAndUpdateIndexDescriptor( reclaimIndex );
 232  
             }
 233  0
             catch ( IOException e )
 234  
             {
 235  0
                 if ( reclaimIndex )
 236  
                 {
 237  0
                     prepareCleanIndex( true );
 238  
                 }
 239  
                 else
 240  
                 {
 241  0
                     throw e;
 242  
                 }
 243  27
             }
 244  
         }
 245  
         else
 246  
         {
 247  453
             prepareCleanIndex( false );
 248  
         }
 249  
 
 250  480
         timestamp = IndexUtils.getTimestamp( indexDirectory );
 251  480
     }
 252  
 
 253  
     private void prepareCleanIndex( boolean deleteExisting )
 254  
         throws IOException
 255  
     {
 256  453
         if ( deleteExisting )
 257  
         {
 258  0
             closeReaders();
 259  
 
 260  
             // unlock the dir forcibly
 261  0
             if ( IndexWriter.isLocked( indexDirectory ) )
 262  
             {
 263  0
                 IndexWriter.unlock( indexDirectory );
 264  
             }
 265  
 
 266  0
             deleteIndexFiles( true );
 267  
         }
 268  
 
 269  453
         openAndWarmup();
 270  
 
 271  453
         if ( StringUtils.isEmpty( getRepositoryId() ) )
 272  
         {
 273  0
             throw new IllegalArgumentException( "The repositoryId cannot be null when creating new repository!" );
 274  
         }
 275  
 
 276  453
         storeDescriptor();
 277  453
     }
 278  
 
 279  
     private void checkAndUpdateIndexDescriptor( boolean reclaimIndex )
 280  
         throws IOException, UnsupportedExistingLuceneIndexException
 281  
     {
 282  27
         if ( reclaimIndex )
 283  
         {
 284  
             // forcefully "reclaiming" the ownership of the index as ours
 285  15
             storeDescriptor();
 286  15
             return;
 287  
         }
 288  
 
 289  
         // check for descriptor if this is not a "virgin" index
 290  12
         if ( getIndexReader().numDocs() > 0 )
 291  
         {
 292  12
             TopScoreDocCollector collector = TopScoreDocCollector.create( 1, false );
 293  
 
 294  12
             getIndexSearcher().search( new TermQuery( DESCRIPTOR_TERM ), collector );
 295  
 
 296  12
             if ( collector.getTotalHits() == 0 )
 297  
             {
 298  0
                 throw new UnsupportedExistingLuceneIndexException( "The existing index has no NexusIndexer descriptor" );
 299  
             }
 300  
 
 301  12
             if ( collector.getTotalHits() > 1 )
 302  
             {
 303  
                 // eh? this is buggy index it seems, just iron it out then
 304  0
                 storeDescriptor();
 305  0
                 return;
 306  
             }
 307  
             else
 308  
             {
 309  
                 // good, we have one descriptor as should
 310  12
                 Document descriptor = getIndexSearcher().doc( collector.topDocs().scoreDocs[0].doc );
 311  12
                 String[] h = StringUtils.split( descriptor.get( FLD_IDXINFO ), ArtifactInfo.FS );
 312  
                 // String version = h[0];
 313  12
                 String repoId = h[1];
 314  
 
 315  
                 // // compare version
 316  
                 // if ( !VERSION.equals( version ) )
 317  
                 // {
 318  
                 // throw new UnsupportedExistingLuceneIndexException(
 319  
                 // "The existing index has version [" + version + "] and not [" + VERSION + "] version!" );
 320  
                 // }
 321  
 
 322  12
                 if ( getRepositoryId() == null )
 323  
                 {
 324  0
                     repositoryId = repoId;
 325  
                 }
 326  12
                 else if ( !getRepositoryId().equals( repoId ) )
 327  
                 {
 328  0
                     throw new UnsupportedExistingLuceneIndexException( "The existing index is for repository " //
 329  
                         + "[" + repoId + "] and not for repository [" + getRepositoryId() + "]" );
 330  
                 }
 331  
             }
 332  
         }
 333  12
     }
 334  
 
 335  
     private void storeDescriptor()
 336  
         throws IOException
 337  
     {
 338  721
         Document hdr = new Document();
 339  
 
 340  721
         hdr.add( new Field( FLD_DESCRIPTOR, FLD_DESCRIPTOR_CONTENTS, Field.Store.YES, Field.Index.NOT_ANALYZED ) );
 341  
 
 342  721
         hdr.add( new Field( FLD_IDXINFO, VERSION + ArtifactInfo.FS + getRepositoryId(), Field.Store.YES, Field.Index.NO ) );
 343  
 
 344  721
         IndexWriter w = getIndexWriter();
 345  
 
 346  721
         w.updateDocument( DESCRIPTOR_TERM, hdr );
 347  
 
 348  721
         w.commit();
 349  721
     }
 350  
 
 351  
     private void deleteIndexFiles( boolean full )
 352  
         throws IOException
 353  
     {
 354  556
         if ( indexDirectory != null )
 355  
         {
 356  556
             String[] names = indexDirectory.listAll();
 357  
 
 358  556
             if ( names != null )
 359  
             {
 360  556
                 IndexFileNameFilter filter = IndexFileNameFilter.getFilter();
 361  
 
 362  6912
                 for ( int i = 0; i < names.length; i++ )
 363  
                 {
 364  6356
                     if ( filter.accept( null, names[i] ) )
 365  
                     {
 366  5848
                         indexDirectory.deleteFile( names[i] );
 367  
                     }
 368  
                 }
 369  
             }
 370  
 
 371  556
             if ( full )
 372  
             {
 373  303
                 if ( indexDirectory.fileExists( INDEX_PACKER_PROPERTIES_FILE ) )
 374  
                 {
 375  21
                     indexDirectory.deleteFile( INDEX_PACKER_PROPERTIES_FILE );
 376  
                 }
 377  
 
 378  303
                 if ( indexDirectory.fileExists( INDEX_UPDATER_PROPERTIES_FILE ) )
 379  
                 {
 380  11
                     indexDirectory.deleteFile( INDEX_UPDATER_PROPERTIES_FILE );
 381  
                 }
 382  
             }
 383  
 
 384  556
             IndexUtils.deleteTimestamp( indexDirectory );
 385  
         }
 386  556
     }
 387  
 
 388  
     public boolean isSearchable()
 389  
     {
 390  204
         return searchable;
 391  
     }
 392  
 
 393  
     public void setSearchable( boolean searchable )
 394  
     {
 395  8
         this.searchable = searchable;
 396  8
     }
 397  
 
 398  
     public String getId()
 399  
     {
 400  69115
         return id;
 401  
     }
 402  
 
 403  
     public void updateTimestamp()
 404  
         throws IOException
 405  
     {
 406  11575
         updateTimestamp( false );
 407  11575
     }
 408  
 
 409  
     public void updateTimestamp( boolean save )
 410  
         throws IOException
 411  
     {
 412  11998
         updateTimestamp( save, new Date() );
 413  11998
     }
 414  
 
 415  
     public void updateTimestamp( boolean save, Date timestamp )
 416  
         throws IOException
 417  
     {
 418  12260
         this.timestamp = timestamp;
 419  
 
 420  12260
         if ( save )
 421  
         {
 422  685
             IndexUtils.updateTimestamp( indexDirectory, getTimestamp() );
 423  
         }
 424  12260
     }
 425  
 
 426  
     public Date getTimestamp()
 427  
     {
 428  2667
         return timestamp;
 429  
     }
 430  
 
 431  
     public int getSize()
 432  
         throws IOException
 433  
     {
 434  0
         return getIndexReader().numDocs();
 435  
     }
 436  
 
 437  
     public String getRepositoryId()
 438  
     {
 439  78263
         return repositoryId;
 440  
     }
 441  
 
 442  
     public File getRepository()
 443  
     {
 444  28501
         return repository;
 445  
     }
 446  
 
 447  
     public String getRepositoryUrl()
 448  
     {
 449  216
         return repositoryUrl;
 450  
     }
 451  
 
 452  
     public String getIndexUpdateUrl()
 453  
     {
 454  232
         if ( repositoryUrl != null )
 455  
         {
 456  28
             if ( indexUpdateUrl == null || indexUpdateUrl.trim().length() == 0 )
 457  
             {
 458  20
                 return repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + INDEX_DIRECTORY;
 459  
             }
 460  
         }
 461  212
         return indexUpdateUrl;
 462  
     }
 463  
 
 464  
     public Analyzer getAnalyzer()
 465  
     {
 466  20
         return new NexusAnalyzer();
 467  
     }
 468  
 
 469  
     protected void openAndWarmup()
 470  
         throws IOException
 471  
     {
 472  
         // IndexWriter (close)
 473  738
         if ( indexWriter != null )
 474  
         {
 475  5
             indexWriter.close();
 476  
 
 477  5
             indexWriter = null;
 478  
         }
 479  
         // IndexSearcher (close only, since we did supply this.indexReader explicitly)
 480  738
         if ( indexSearcher != null )
 481  
         {
 482  5
             indexSearcher.close();
 483  
 
 484  5
             indexSearcher = null;
 485  
         }
 486  
         // IndexReader
 487  738
         if ( indexReader != null )
 488  
         {
 489  5
             indexReader.close();
 490  
 
 491  5
             indexReader = null;
 492  
         }
 493  
 
 494  
         // IndexWriter open
 495  738
         final boolean create = !IndexReader.indexExists( indexDirectory );
 496  
 
 497  738
         indexWriter = new NexusIndexWriter( getIndexDirectory(), new NexusAnalyzer(), create );
 498  
 
 499  738
         indexWriter.setRAMBufferSizeMB( 2 );
 500  
 
 501  738
         indexWriter.setMergeScheduler( new SerialMergeScheduler() );
 502  
 
 503  738
         indexWriter.commit(); // LUCENE-2386
 504  
 
 505  738
         openAndWarmupReaders();
 506  738
     }
 507  
 
 508  
     protected void openAndWarmupReaders()
 509  
         throws IOException
 510  
     {
 511  7071
         if ( indexReader != null && indexReader.isCurrent() )
 512  
         {
 513  63
             return;
 514  
         }
 515  
 
 516  
         // IndexReader open
 517  7008
         IndexReader newIndexReader = IndexReader.open( indexDirectory, true );
 518  
 
 519  
         // IndexSearcher open, but with new reader
 520  7008
         NexusIndexSearcher newIndexSearcher = new NexusIndexSearcher( this, newIndexReader );
 521  
 
 522  
         // warm up
 523  7008
         warmUp( newIndexSearcher );
 524  
 
 525  7008
         lockExclusively();
 526  
 
 527  
         try
 528  
         {
 529  
             // IndexSearcher (close only, since we did supply this.indexReader explicitly)
 530  7008
             if ( indexSearcher != null )
 531  
             {
 532  6270
                 indexSearcher.close();
 533  
             }
 534  
             // IndexReader
 535  7008
             if ( indexReader != null )
 536  
             {
 537  6270
                 indexReader.close();
 538  
             }
 539  
 
 540  7008
             indexReader = newIndexReader;
 541  
 
 542  7008
             indexSearcher = newIndexSearcher;
 543  
         }
 544  
         finally
 545  
         {
 546  7008
             unlockExclusively();
 547  7008
         }
 548  7008
     }
 549  
 
 550  
     protected void warmUp( NexusIndexSearcher searcher )
 551  
         throws IOException
 552  
     {
 553  
         try
 554  
         {
 555  
             // TODO: figure this out better and non blocking
 556  7008
             searcher.search( new TermQuery( new Term( "g", "org.apache" ) ), 1000 );
 557  
         }
 558  0
         catch ( IOException e )
 559  
         {
 560  0
             close( false );
 561  
 
 562  0
             throw e;
 563  7008
         }
 564  7008
     }
 565  
 
 566  
     public IndexWriter getIndexWriter()
 567  
         throws IOException
 568  
     {
 569  20357
         lock();
 570  
 
 571  
         try
 572  
         {
 573  20357
             return indexWriter;
 574  
         }
 575  
         finally
 576  
         {
 577  20357
             unlock();
 578  
         }
 579  
     }
 580  
 
 581  
     public IndexReader getIndexReader()
 582  
         throws IOException
 583  
     {
 584  283212
         lock();
 585  
 
 586  
         try
 587  
         {
 588  283212
             return indexReader;
 589  
         }
 590  
         finally
 591  
         {
 592  283212
             unlock();
 593  
         }
 594  
     }
 595  
 
 596  
     public IndexSearcher getIndexSearcher()
 597  
         throws IOException
 598  
     {
 599  96674
         lock();
 600  
 
 601  
         try
 602  
         {
 603  96674
             return indexSearcher;
 604  
         }
 605  
         finally
 606  
         {
 607  96674
             unlock();
 608  
         }
 609  
     }
 610  
 
 611  
     public void commit()
 612  
         throws IOException
 613  
     {
 614  
         // TODO: detect is writer "dirty"?
 615  
         if ( true )
 616  
         {
 617  5641
             if ( BLOCKING_COMMIT )
 618  
             {
 619  5105
                 lockExclusively();
 620  
             }
 621  
             else
 622  
             {
 623  536
                 lock();
 624  
             }
 625  
 
 626  
             try
 627  
             {
 628  5641
                 doCommit( BLOCKING_COMMIT );
 629  
             }
 630  
             finally
 631  
             {
 632  5641
                 if ( BLOCKING_COMMIT )
 633  
                 {
 634  5105
                     unlockExclusively();
 635  
                 }
 636  
                 else
 637  
                 {
 638  536
                     unlock();
 639  
                 }
 640  536
             }
 641  
         }
 642  5641
     }
 643  
 
 644  
     protected void doCommit( boolean blocking )
 645  
         throws IOException
 646  
     {
 647  
         try
 648  
         {
 649  
             // TODO: is this needed? Why not put the commit() call into synchronized
 650  
             // since all callers of doCommit() aside of commit() already possess exclusive lock
 651  6851
             synchronized ( this )
 652  
             {
 653  6851
                 getIndexWriter().commit();
 654  6851
             }
 655  
 
 656  
             // TODO: define some treshold or requirement
 657  
             // for reopening readers (is expensive)
 658  
             // For example: by inserting 1 record among 1M, do we really want to reopen?
 659  
             if ( true )
 660  
             {
 661  6851
                 if ( blocking )
 662  
                 {
 663  6315
                     openAndWarmupReaders();
 664  
                 }
 665  
                 else
 666  
                 {
 667  536
                     flagNeedsReopen();
 668  
                 }
 669  
             }
 670  
         }
 671  0
         catch ( CorruptIndexException e )
 672  
         {
 673  0
             close( false );
 674  
 
 675  0
             throw e;
 676  
         }
 677  0
         catch ( IOException e )
 678  
         {
 679  0
             close( false );
 680  
 
 681  0
             throw e;
 682  6851
         }
 683  6851
     }
 684  
 
 685  
     public void rollback()
 686  
         throws IOException
 687  
     {
 688  
         // detect is writer "dirty"?
 689  
         if ( true )
 690  
         {
 691  0
             lock();
 692  
 
 693  
             try
 694  
             {
 695  0
                 IndexWriter w = getIndexWriter();
 696  
 
 697  
                 try
 698  
                 {
 699  0
                     synchronized ( this )
 700  
                     {
 701  0
                         w.rollback();
 702  0
                     }
 703  
                 }
 704  0
                 catch ( CorruptIndexException e )
 705  
                 {
 706  0
                     close( false );
 707  
 
 708  0
                     throw e;
 709  
                 }
 710  0
                 catch ( IOException e )
 711  
                 {
 712  0
                     close( false );
 713  
 
 714  0
                     throw e;
 715  0
                 }
 716  
             }
 717  
             finally
 718  
             {
 719  0
                 unlock();
 720  0
             }
 721  
         }
 722  0
     }
 723  
 
 724  
     public void optimize()
 725  
         throws CorruptIndexException, IOException
 726  
     {
 727  696
         lockExclusively();
 728  
 
 729  
         try
 730  
         {
 731  696
             IndexWriter w = getIndexWriter();
 732  
 
 733  
             try
 734  
             {
 735  696
                 w.optimize();
 736  
 
 737  696
                 doCommit( true );
 738  
             }
 739  0
             catch ( CorruptIndexException e )
 740  
             {
 741  0
                 close( false );
 742  
 
 743  0
                 throw e;
 744  
             }
 745  0
             catch ( IOException e )
 746  
             {
 747  0
                 close( false );
 748  
 
 749  0
                 throw e;
 750  696
             }
 751  
         }
 752  
         finally
 753  
         {
 754  696
             unlockExclusively();
 755  696
         }
 756  696
     }
 757  
 
 758  
     public void close( boolean deleteFiles )
 759  
         throws IOException
 760  
     {
 761  437
         lockExclusively();
 762  
 
 763  
         try
 764  
         {
 765  437
             if ( indexDirectory != null )
 766  
             {
 767  437
                 IndexUtils.updateTimestamp( indexDirectory, getTimestamp() );
 768  
 
 769  437
                 closeReaders();
 770  
 
 771  437
                 if ( deleteFiles )
 772  
                 {
 773  298
                     deleteIndexFiles( true );
 774  
                 }
 775  
 
 776  437
                 indexDirectory.close();
 777  
             }
 778  
 
 779  
             // TODO: this will prevent from reopening them, but needs better solution
 780  
             // Needed to make bottleWarmerThread die off
 781  437
             indexDirectory = null;
 782  
         }
 783  
         finally
 784  
         {
 785  437
             unlockExclusively();
 786  437
         }
 787  437
     }
 788  
 
 789  
     public void purge()
 790  
         throws IOException
 791  
     {
 792  5
         lockExclusively();
 793  
 
 794  
         try
 795  
         {
 796  5
             closeReaders();
 797  
 
 798  5
             deleteIndexFiles( true );
 799  
 
 800  5
             openAndWarmup();
 801  
 
 802  
             try
 803  
             {
 804  5
                 prepareIndex( true );
 805  
             }
 806  0
             catch ( UnsupportedExistingLuceneIndexException e )
 807  
             {
 808  
                 // just deleted it
 809  5
             }
 810  
 
 811  5
             rebuildGroups();
 812  
 
 813  5
             updateTimestamp( true, null );
 814  
         }
 815  
         finally
 816  
         {
 817  5
             unlockExclusively();
 818  5
         }
 819  5
     }
 820  
 
 821  
     public void replace( Directory directory )
 822  
         throws IOException
 823  
     {
 824  253
         lockExclusively();
 825  
 
 826  
         try
 827  
         {
 828  253
             Date ts = IndexUtils.getTimestamp( directory );
 829  
 
 830  253
             closeReaders();
 831  
 
 832  253
             deleteIndexFiles( false );
 833  
 
 834  253
             IndexUtils.copyDirectory( directory, indexDirectory );
 835  
 
 836  253
             openAndWarmup();
 837  
 
 838  
             // reclaim the index as mine
 839  253
             storeDescriptor();
 840  
 
 841  253
             updateTimestamp( true, ts );
 842  
 
 843  253
             optimize();
 844  
         }
 845  
         finally
 846  
         {
 847  253
             unlockExclusively();
 848  253
         }
 849  253
     }
 850  
 
 851  
     public void merge( Directory directory )
 852  
         throws IOException
 853  
     {
 854  8
         merge( directory, null );
 855  8
     }
 856  
 
 857  
     public void merge( Directory directory, DocumentFilter filter )
 858  
         throws IOException
 859  
     {
 860  8
         lockExclusively();
 861  
 
 862  
         try
 863  
         {
 864  8
             IndexWriter w = getIndexWriter();
 865  
 
 866  8
             IndexSearcher s = getIndexSearcher();
 867  
 
 868  8
             IndexReader directoryReader = IndexReader.open( directory, true );
 869  
 
 870  8
             TopScoreDocCollector collector = null;
 871  
 
 872  
             try
 873  
             {
 874  8
                 int numDocs = directoryReader.maxDoc();
 875  
 
 876  60
                 for ( int i = 0; i < numDocs; i++ )
 877  
                 {
 878  52
                     if ( directoryReader.isDeleted( i ) )
 879  
                     {
 880  2
                         continue;
 881  
                     }
 882  
 
 883  50
                     Document d = directoryReader.document( i );
 884  
 
 885  50
                     if ( filter != null && !filter.accept( d ) )
 886  
                     {
 887  0
                         continue;
 888  
                     }
 889  
 
 890  50
                     String uinfo = d.get( ArtifactInfo.UINFO );
 891  
 
 892  50
                     if ( uinfo != null )
 893  
                     {
 894  27
                         collector = TopScoreDocCollector.create( 1, false );
 895  
 
 896  27
                         s.search( new TermQuery( new Term( ArtifactInfo.UINFO, uinfo ) ), collector );
 897  
 
 898  27
                         if ( collector.getTotalHits() == 0 )
 899  
                         {
 900  17
                             w.addDocument( IndexUtils.updateDocument( d, this, false ) );
 901  
                         }
 902  
                     }
 903  
                     else
 904  
                     {
 905  23
                         String deleted = d.get( ArtifactInfo.DELETED );
 906  
 
 907  23
                         if ( deleted != null )
 908  
                         {
 909  
                             // Deleting the document loses history that it was delete,
 910  
                             // so incrementals wont work. Therefore, put the delete
 911  
                             // document in as well
 912  2
                             w.deleteDocuments( new Term( ArtifactInfo.UINFO, deleted ) );
 913  2
                             w.addDocument( d );
 914  
                         }
 915  
                     }
 916  
                 }
 917  
 
 918  
             }
 919  
             finally
 920  
             {
 921  8
                 directoryReader.close();
 922  
 
 923  8
                 doCommit( true );
 924  8
             }
 925  
 
 926  8
             rebuildGroups();
 927  
 
 928  8
             Date mergedTimestamp = IndexUtils.getTimestamp( directory );
 929  
 
 930  8
             if ( getTimestamp() != null && mergedTimestamp != null && mergedTimestamp.after( getTimestamp() ) )
 931  
             {
 932  
                 // we have both, keep the newest
 933  4
                 updateTimestamp( true, mergedTimestamp );
 934  
             }
 935  
             else
 936  
             {
 937  4
                 updateTimestamp( true );
 938  
             }
 939  
 
 940  8
             optimize();
 941  
         }
 942  
         finally
 943  
         {
 944  8
             unlockExclusively();
 945  8
         }
 946  8
     }
 947  
 
 948  
     private void closeReaders()
 949  
         throws CorruptIndexException, IOException
 950  
     {
 951  695
         if ( indexWriter != null )
 952  
         {
 953  695
             indexWriter.close();
 954  
 
 955  695
             indexWriter = null;
 956  
         }
 957  695
         if ( indexSearcher != null )
 958  
         {
 959  695
             indexSearcher.close();
 960  
 
 961  695
             indexSearcher = null;
 962  
         }
 963  695
         if ( indexReader != null )
 964  
         {
 965  695
             indexReader.close();
 966  
 
 967  695
             indexReader = null;
 968  
         }
 969  695
     }
 970  
 
 971  
     public GavCalculator getGavCalculator()
 972  
     {
 973  21945
         return gavCalculator;
 974  
     }
 975  
 
 976  
     public List<IndexCreator> getIndexCreators()
 977  
     {
 978  438019
         return Collections.unmodifiableList( indexCreators );
 979  
     }
 980  
 
 981  
     // groups
 982  
 
 983  
     public void rebuildGroups()
 984  
         throws IOException
 985  
     {
 986  13
         lockExclusively();
 987  
 
 988  
         try
 989  
         {
 990  13
             IndexReader r = getIndexReader();
 991  
 
 992  13
             Set<String> rootGroups = new LinkedHashSet<String>();
 993  13
             Set<String> allGroups = new LinkedHashSet<String>();
 994  
 
 995  13
             int numDocs = r.maxDoc();
 996  
 
 997  83
             for ( int i = 0; i < numDocs; i++ )
 998  
             {
 999  70
                 if ( r.isDeleted( i ) )
 1000  
                 {
 1001  3
                     continue;
 1002  
                 }
 1003  
 
 1004  67
                 Document d = r.document( i );
 1005  
 
 1006  67
                 String uinfo = d.get( ArtifactInfo.UINFO );
 1007  
 
 1008  67
                 if ( uinfo != null )
 1009  
                 {
 1010  41
                     ArtifactInfo info = IndexUtils.constructArtifactInfo( d, this );
 1011  41
                     rootGroups.add( info.getRootGroup() );
 1012  41
                     allGroups.add( info.groupId );
 1013  
                 }
 1014  
             }
 1015  
 
 1016  13
             setRootGroups( rootGroups );
 1017  13
             setAllGroups( allGroups );
 1018  
 
 1019  13
             optimize();
 1020  
         }
 1021  
         finally
 1022  
         {
 1023  13
             unlockExclusively();
 1024  13
         }
 1025  13
     }
 1026  
 
 1027  
     public Set<String> getAllGroups()
 1028  
         throws IOException
 1029  
     {
 1030  5631
         lock();
 1031  
 
 1032  
         try
 1033  
         {
 1034  5631
             return getGroups( ArtifactInfo.ALL_GROUPS, ArtifactInfo.ALL_GROUPS_VALUE, ArtifactInfo.ALL_GROUPS_LIST );
 1035  
         }
 1036  
         finally
 1037  
         {
 1038  5631
             unlock();
 1039  
         }
 1040  
     }
 1041  
 
 1042  
     public void setAllGroups( Collection<String> groups )
 1043  
         throws IOException
 1044  
     {
 1045  255
         lockExclusively();
 1046  
 
 1047  
         try
 1048  
         {
 1049  255
             setGroups( groups, ArtifactInfo.ALL_GROUPS, ArtifactInfo.ALL_GROUPS_VALUE, ArtifactInfo.ALL_GROUPS_LIST );
 1050  
 
 1051  255
             doCommit( true );
 1052  
         }
 1053  
         finally
 1054  
         {
 1055  255
             unlockExclusively();
 1056  255
         }
 1057  255
     }
 1058  
 
 1059  
     public Set<String> getRootGroups()
 1060  
         throws IOException
 1061  
     {
 1062  5602
         lock();
 1063  
 
 1064  
         try
 1065  
         {
 1066  5602
             return getGroups( ArtifactInfo.ROOT_GROUPS, ArtifactInfo.ROOT_GROUPS_VALUE, ArtifactInfo.ROOT_GROUPS_LIST );
 1067  
         }
 1068  
         finally
 1069  
         {
 1070  5602
             unlock();
 1071  
         }
 1072  
     }
 1073  
 
 1074  
     public void setRootGroups( Collection<String> groups )
 1075  
         throws IOException
 1076  
     {
 1077  251
         lockExclusively();
 1078  
 
 1079  
         try
 1080  
         {
 1081  251
             setGroups( groups, ArtifactInfo.ROOT_GROUPS, ArtifactInfo.ROOT_GROUPS_VALUE, ArtifactInfo.ROOT_GROUPS_LIST );
 1082  
 
 1083  251
             doCommit( true );
 1084  
         }
 1085  
         finally
 1086  
         {
 1087  251
             unlockExclusively();
 1088  251
         }
 1089  251
     }
 1090  
 
 1091  
     protected Set<String> getGroups( String field, String filedValue, String listField )
 1092  
         throws IOException, CorruptIndexException
 1093  
     {
 1094  11233
         TopScoreDocCollector collector = TopScoreDocCollector.create( 1, false );
 1095  
 
 1096  11233
         getIndexSearcher().search( new TermQuery( new Term( field, filedValue ) ), collector );
 1097  
 
 1098  11233
         TopDocs topDocs = collector.topDocs();
 1099  
 
 1100  11233
         Set<String> groups = new LinkedHashSet<String>( Math.max( 10, topDocs.totalHits ) );
 1101  
 
 1102  11233
         if ( topDocs.totalHits > 0 )
 1103  
         {
 1104  11201
             Document doc = getIndexSearcher().doc( topDocs.scoreDocs[0].doc );
 1105  
 
 1106  11201
             String groupList = doc.get( listField );
 1107  
 
 1108  11201
             if ( groupList != null )
 1109  
             {
 1110  11201
                 groups.addAll( Arrays.asList( groupList.split( "\\|" ) ) );
 1111  
             }
 1112  
         }
 1113  
 
 1114  11233
         return groups;
 1115  
     }
 1116  
 
 1117  
     protected void setGroups( Collection<String> groups, String groupField, String groupFieldValue,
 1118  
                               String groupListField )
 1119  
         throws IOException, CorruptIndexException
 1120  
     {
 1121  506
         IndexWriter w = getIndexWriter();
 1122  
 
 1123  506
         w.updateDocument( new Term( groupField, groupFieldValue ),
 1124  
             createGroupsDocument( groups, groupField, groupFieldValue, groupListField ) );
 1125  506
     }
 1126  
 
 1127  
     protected Document createGroupsDocument( Collection<String> groups, String field, String fieldValue,
 1128  
                                              String listField )
 1129  
     {
 1130  506
         Document groupDoc = new Document();
 1131  
 
 1132  506
         groupDoc.add( new Field( field, //
 1133  
             fieldValue, Field.Store.YES, Field.Index.NOT_ANALYZED ) );
 1134  
 
 1135  506
         groupDoc.add( new Field( listField, //
 1136  
             ArtifactInfo.lst2str( groups ), Field.Store.YES, Field.Index.NO ) );
 1137  
 
 1138  506
         return groupDoc;
 1139  
     }
 1140  
 
 1141  
     @Override
 1142  
     public String toString()
 1143  
     {
 1144  0
         return id + " : " + timestamp;
 1145  
     }
 1146  
 
 1147  
     // ==
 1148  
 
 1149  475
     private volatile boolean needsReaderReopen = false;
 1150  
 
 1151  
     protected void flagNeedsReopen()
 1152  
     {
 1153  536
         needsReaderReopen = true;
 1154  536
     }
 1155  
 
 1156  
     protected void unflagNeedsReopen()
 1157  
     {
 1158  18
         needsReaderReopen = false;
 1159  18
     }
 1160  
 
 1161  
     protected boolean isReopenNeeded()
 1162  
     {
 1163  390
         return needsReaderReopen;
 1164  
     }
 1165  
 
 1166  
     protected void installBottleWarmer()
 1167  
     {
 1168  475
         if ( BLOCKING_COMMIT )
 1169  
         {
 1170  447
             return;
 1171  
         }
 1172  
 
 1173  28
         Runnable bottleWarmer = new Runnable()
 1174  28
         {
 1175  
             public void run()
 1176  
             {
 1177  
                 // die off when context is closed
 1178  410
                 while ( indexDirectory != null )
 1179  
                 {
 1180  
                     try
 1181  
                     {
 1182  390
                         if ( isReopenNeeded() )
 1183  
                         {
 1184  18
                             openAndWarmupReaders();
 1185  
 
 1186  18
                             unflagNeedsReopen();
 1187  
                         }
 1188  
 
 1189  390
                         Thread.sleep( 1000 );
 1190  
                     }
 1191  0
                     catch ( Exception e )
 1192  
                     {
 1193  0
                         e.printStackTrace();
 1194  382
                     }
 1195  
                 }
 1196  20
             }
 1197  
         };
 1198  
 
 1199  28
         bottleWarmerThread = new Thread( bottleWarmer, "Index-BottleWarmer-" + id );
 1200  28
         bottleWarmerThread.setDaemon( true );
 1201  28
         bottleWarmerThread.start();
 1202  28
     }
 1203  
 
 1204  
     /**
 1205  
      * A flag useful for tests, to make this IndexingContext implementation blocking. If this flag is true, context will
 1206  
      * block the commit() calls and will return from it when Lucene commit done AND all the readers are reopened and are
 1207  
      * current. TODO: this is currently inherently unsafe (is not final), and is meant to be used in Unit tests only!
 1208  
      * Think something and tie this knot properly.
 1209  
      */
 1210  1
     public static boolean BLOCKING_COMMIT = false;
 1211  
 }