add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / src / test / org / apache / lucene / index / TestIndexReaderReopen.java
1 package org.apache.lucene.index;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Random;
30 import java.util.Set;
31
32 import org.apache.lucene.analysis.MockAnalyzer;
33 import org.apache.lucene.document.Document;
34 import org.apache.lucene.document.Field.Index;
35 import org.apache.lucene.document.Field.Store;
36 import org.apache.lucene.document.Field;
37 import org.apache.lucene.index.IndexWriterConfig.OpenMode;
38 import org.apache.lucene.search.FieldCache;
39 import org.apache.lucene.search.IndexSearcher;
40 import org.apache.lucene.search.ScoreDoc;
41 import org.apache.lucene.search.TermQuery;
42 import org.apache.lucene.store.AlreadyClosedException;
43 import org.apache.lucene.store.Directory;
44 import org.apache.lucene.util.BitVector;
45 import org.apache.lucene.util.LuceneTestCase;
46 import org.apache.lucene.util._TestUtil;
47
48 public class TestIndexReaderReopen extends LuceneTestCase {
49   
50   public void testReopen() throws Exception {
51     final Directory dir1 = newDirectory();
52     
53     createIndex(random, dir1, false);
54     performDefaultTests(new TestReopen() {
55
56       @Override
57       protected void modifyIndex(int i) throws IOException {
58         TestIndexReaderReopen.modifyIndex(i, dir1);
59       }
60
61       @Override
62       protected IndexReader openReader() throws IOException {
63         return IndexReader.open(dir1, false);
64       }
65       
66     });
67     dir1.close();
68     
69     final Directory dir2 = newDirectory();
70     
71     createIndex(random, dir2, true);
72     performDefaultTests(new TestReopen() {
73
74       @Override
75       protected void modifyIndex(int i) throws IOException {
76         TestIndexReaderReopen.modifyIndex(i, dir2);
77       }
78
79       @Override
80       protected IndexReader openReader() throws IOException {
81         return IndexReader.open(dir2, false);
82       }
83       
84     });
85     dir2.close();
86   }
87   
88   public void testParallelReaderReopen() throws Exception {
89     final Directory dir1 = newDirectory();
90     createIndex(random, dir1, true);
91     final Directory dir2 = newDirectory();
92     createIndex(random, dir2, true);
93     
94     performDefaultTests(new TestReopen() {
95
96       @Override
97       protected void modifyIndex(int i) throws IOException {
98         TestIndexReaderReopen.modifyIndex(i, dir1);
99         TestIndexReaderReopen.modifyIndex(i, dir2);
100       }
101
102       @Override
103       protected IndexReader openReader() throws IOException {
104         ParallelReader pr = new ParallelReader();
105         pr.add(IndexReader.open(dir1, false));
106         pr.add(IndexReader.open(dir2, false));
107         return pr;
108       }
109       
110     });
111     dir1.close();
112     dir2.close();
113     
114     final Directory dir3 = newDirectory();
115     createIndex(random, dir3, true);
116     final Directory dir4 = newDirectory();
117     createIndex(random, dir4, true);
118
119     performTestsWithExceptionInReopen(new TestReopen() {
120
121       @Override
122       protected void modifyIndex(int i) throws IOException {
123         TestIndexReaderReopen.modifyIndex(i, dir3);
124         TestIndexReaderReopen.modifyIndex(i, dir4);
125       }
126
127       @Override
128       protected IndexReader openReader() throws IOException {
129         ParallelReader pr = new ParallelReader();
130         pr.add(IndexReader.open(dir3, false));
131         pr.add(IndexReader.open(dir4, false));
132         // Does not implement reopen, so
133         // hits exception:
134         pr.add(new FilterIndexReader(IndexReader.open(dir3, false)));
135         return pr;
136       }
137       
138     });
139     dir3.close();
140     dir4.close();
141   }
142
143   // LUCENE-1228: IndexWriter.commit() does not update the index version
144   // populate an index in iterations.
145   // at the end of every iteration, commit the index and reopen/recreate the reader.
146   // in each iteration verify the work of previous iteration. 
147   // try this once with reopen once recreate, on both RAMDir and FSDir.
148   public void testCommitReopen () throws IOException {
149     Directory dir = newDirectory();
150     doTestReopenWithCommit(random, dir, true);
151     dir.close();
152   }
153   public void testCommitRecreate () throws IOException {
154     Directory dir = newDirectory();
155     doTestReopenWithCommit(random, dir, false);
156     dir.close();
157   }
158
159   private void doTestReopenWithCommit (Random random, Directory dir, boolean withReopen) throws IOException {
160     IndexWriter iwriter = new IndexWriter(dir, newIndexWriterConfig(
161         TEST_VERSION_CURRENT, new MockAnalyzer(random)).setOpenMode(
162                                                               OpenMode.CREATE).setMergeScheduler(new SerialMergeScheduler()).setMergePolicy(newLogMergePolicy()));
163     iwriter.commit();
164     IndexReader reader = IndexReader.open(dir, false);
165     try {
166       int M = 3;
167       for (int i=0; i<4; i++) {
168         for (int j=0; j<M; j++) {
169           Document doc = new Document();
170           doc.add(newField("id", i+"_"+j, Store.YES, Index.NOT_ANALYZED));
171           doc.add(newField("id2", i+"_"+j, Store.YES, Index.NOT_ANALYZED_NO_NORMS));
172           doc.add(newField("id3", i+"_"+j, Store.YES, Index.NO));
173           iwriter.addDocument(doc);
174           if (i>0) {
175             int k = i-1;
176             int n = j + k*M;
177             Document prevItereationDoc = reader.document(n);
178             assertNotNull(prevItereationDoc);
179             String id = prevItereationDoc.get("id");
180             assertEquals(k+"_"+j, id);
181           }
182         }
183         iwriter.commit();
184         if (withReopen) {
185           // reopen
186           IndexReader r2 = reader.reopen();
187           if (reader != r2) {
188             reader.close();
189             reader = r2;
190           }
191         } else {
192           // recreate
193           reader.close();
194           reader = IndexReader.open(dir, false);
195         }
196       }
197     } finally {
198       iwriter.close();
199       reader.close();
200     }
201   }
202   
203   public void testMultiReaderReopen() throws Exception {
204     final Directory dir1 = newDirectory();
205     createIndex(random, dir1, true);
206
207     final Directory dir2 = newDirectory();
208     createIndex(random, dir2, true);
209
210     performDefaultTests(new TestReopen() {
211
212       @Override
213       protected void modifyIndex(int i) throws IOException {
214         TestIndexReaderReopen.modifyIndex(i, dir1);
215         TestIndexReaderReopen.modifyIndex(i, dir2);
216       }
217
218       @Override
219       protected IndexReader openReader() throws IOException {
220         return new MultiReader(new IndexReader[] 
221                         {IndexReader.open(dir1, false), 
222                          IndexReader.open(dir2, false)});
223       }
224       
225     });
226
227     dir1.close();
228     dir2.close();
229     
230     final Directory dir3 = newDirectory();
231     createIndex(random, dir3, true);
232
233     final Directory dir4 = newDirectory();
234     createIndex(random, dir4, true);
235
236     performTestsWithExceptionInReopen(new TestReopen() {
237
238       @Override
239       protected void modifyIndex(int i) throws IOException {
240         TestIndexReaderReopen.modifyIndex(i, dir3);
241         TestIndexReaderReopen.modifyIndex(i, dir4);
242       }
243
244       @Override
245       protected IndexReader openReader() throws IOException {
246         return new MultiReader(new IndexReader[] 
247                         {IndexReader.open(dir3, false), 
248                          IndexReader.open(dir4, false),
249                          // Does not implement reopen, so
250                          // hits exception:
251                          new FilterIndexReader(IndexReader.open(dir3, false))});
252       }
253       
254     });
255     dir3.close();
256     dir4.close();
257   }
258
259   public void testMixedReaders() throws Exception {
260     final Directory dir1 = newDirectory();
261     createIndex(random, dir1, true);
262     final Directory dir2 = newDirectory();
263     createIndex(random, dir2, true);
264     final Directory dir3 = newDirectory();
265     createIndex(random, dir3, false);
266     final Directory dir4 = newDirectory();
267     createIndex(random, dir4, true);
268     final Directory dir5 = newDirectory();
269     createIndex(random, dir5, false);
270     
271     performDefaultTests(new TestReopen() {
272
273       @Override
274       protected void modifyIndex(int i) throws IOException {
275         // only change norms in this index to maintain the same number of docs for each of ParallelReader's subreaders
276         if (i == 1) TestIndexReaderReopen.modifyIndex(i, dir1);  
277         
278         TestIndexReaderReopen.modifyIndex(i, dir4);
279         TestIndexReaderReopen.modifyIndex(i, dir5);
280       }
281
282       @Override
283       protected IndexReader openReader() throws IOException {
284         ParallelReader pr = new ParallelReader();
285         pr.add(IndexReader.open(dir1, false));
286         pr.add(IndexReader.open(dir2, false));
287         MultiReader mr = new MultiReader(new IndexReader[] {
288             IndexReader.open(dir3, false), IndexReader.open(dir4, false)});
289         return new MultiReader(new IndexReader[] {
290            pr, mr, IndexReader.open(dir5, false)});
291       }
292     });
293     dir1.close();
294     dir2.close();
295     dir3.close();
296     dir4.close();
297     dir5.close();
298   }  
299   
300   private void performDefaultTests(TestReopen test) throws Exception {
301
302     IndexReader index1 = test.openReader();
303     IndexReader index2 = test.openReader();
304         
305     TestIndexReader.assertIndexEquals(index1, index2);
306
307     // verify that reopen() does not return a new reader instance
308     // in case the index has no changes
309     ReaderCouple couple = refreshReader(index2, false);
310     assertTrue(couple.refreshedReader == index2);
311     
312     couple = refreshReader(index2, test, 0, true);
313     index1.close();
314     index1 = couple.newReader;
315
316     IndexReader index2_refreshed = couple.refreshedReader;
317     index2.close();
318     
319     // test if refreshed reader and newly opened reader return equal results
320     TestIndexReader.assertIndexEquals(index1, index2_refreshed);
321
322     index2_refreshed.close();
323     assertReaderClosed(index2, true, true);
324     assertReaderClosed(index2_refreshed, true, true);
325
326     index2 = test.openReader();
327     
328     for (int i = 1; i < 4; i++) {
329       
330       index1.close();
331       couple = refreshReader(index2, test, i, true);
332       // refresh IndexReader
333       index2.close();
334       
335       index2 = couple.refreshedReader;
336       index1 = couple.newReader;
337       TestIndexReader.assertIndexEquals(index1, index2);
338     }
339     
340     index1.close();
341     index2.close();
342     assertReaderClosed(index1, true, true);
343     assertReaderClosed(index2, true, true);
344   }
345   
346   public void testReferenceCounting() throws IOException {
347     for (int mode = 0; mode < 4; mode++) {
348       Directory dir1 = newDirectory();
349       createIndex(random, dir1, true);
350      
351       IndexReader reader0 = IndexReader.open(dir1, false);
352       assertRefCountEquals(1, reader0);
353
354       assertTrue(reader0 instanceof DirectoryReader);
355       IndexReader[] subReaders0 = reader0.getSequentialSubReaders();
356       for (int i = 0; i < subReaders0.length; i++) {
357         assertRefCountEquals(1, subReaders0[i]);
358       }
359       
360       // delete first document, so that only one of the subReaders have to be re-opened
361       IndexReader modifier = IndexReader.open(dir1, false);
362       modifier.deleteDocument(0);
363       modifier.close();
364       
365       IndexReader reader1 = refreshReader(reader0, true).refreshedReader;
366       assertTrue(reader1 instanceof DirectoryReader);
367       IndexReader[] subReaders1 = reader1.getSequentialSubReaders();
368       assertEquals(subReaders0.length, subReaders1.length);
369       
370       for (int i = 0; i < subReaders0.length; i++) {
371         if (subReaders0[i] != subReaders1[i]) {
372           assertRefCountEquals(1, subReaders0[i]);
373           assertRefCountEquals(1, subReaders1[i]);
374         } else {
375           assertRefCountEquals(2, subReaders0[i]);
376         }
377       }
378
379       // delete first document, so that only one of the subReaders have to be re-opened
380       modifier = IndexReader.open(dir1, false);
381       modifier.deleteDocument(1);
382       modifier.close();
383
384       IndexReader reader2 = refreshReader(reader1, true).refreshedReader;
385       assertTrue(reader2 instanceof DirectoryReader);
386       IndexReader[] subReaders2 = reader2.getSequentialSubReaders();
387       assertEquals(subReaders1.length, subReaders2.length);
388       
389       for (int i = 0; i < subReaders2.length; i++) {
390         if (subReaders2[i] == subReaders1[i]) {
391           if (subReaders1[i] == subReaders0[i]) {
392             assertRefCountEquals(3, subReaders2[i]);
393           } else {
394             assertRefCountEquals(2, subReaders2[i]);
395           }
396         } else {
397           assertRefCountEquals(1, subReaders2[i]);
398           if (subReaders0[i] == subReaders1[i]) {
399             assertRefCountEquals(2, subReaders2[i]);
400             assertRefCountEquals(2, subReaders0[i]);
401           } else {
402             assertRefCountEquals(1, subReaders0[i]);
403             assertRefCountEquals(1, subReaders1[i]);
404           }
405         }
406       }
407       
408       IndexReader reader3 = refreshReader(reader0, true).refreshedReader;
409       assertTrue(reader3 instanceof DirectoryReader);
410       IndexReader[] subReaders3 = reader3.getSequentialSubReaders();
411       assertEquals(subReaders3.length, subReaders0.length);
412       
413       // try some permutations
414       switch (mode) {
415       case 0:
416         reader0.close();
417         reader1.close();
418         reader2.close();
419         reader3.close();
420         break;
421       case 1:
422         reader3.close();
423         reader2.close();
424         reader1.close();
425         reader0.close();
426         break;
427       case 2:
428         reader2.close();
429         reader3.close();
430         reader0.close();
431         reader1.close();
432         break;
433       case 3:
434         reader1.close();
435         reader3.close();
436         reader2.close();
437         reader0.close();
438         break;
439       }      
440       
441       assertReaderClosed(reader0, true, true);
442       assertReaderClosed(reader1, true, true);
443       assertReaderClosed(reader2, true, true);
444       assertReaderClosed(reader3, true, true);
445
446       dir1.close();
447     }
448   }
449
450
451   public void testReferenceCountingMultiReader() throws IOException {
452     for (int mode = 0; mode <=1; mode++) {
453       Directory dir1 = newDirectory();
454       createIndex(random, dir1, false);
455       Directory dir2 = newDirectory();
456       createIndex(random, dir2, true);
457       
458       IndexReader reader1 = IndexReader.open(dir1, false);
459       assertRefCountEquals(1, reader1);
460
461       IndexReader initReader2 = IndexReader.open(dir2, false);
462       IndexReader multiReader1 = new MultiReader(new IndexReader[] {reader1, initReader2}, (mode == 0));
463       modifyIndex(0, dir2);
464       assertRefCountEquals(1 + mode, reader1);
465       
466       IndexReader multiReader2 = multiReader1.reopen();
467       // index1 hasn't changed, so multiReader2 should share reader1 now with multiReader1
468       assertRefCountEquals(2 + mode, reader1);
469       
470       modifyIndex(0, dir1);
471       IndexReader reader2 = reader1.reopen();
472       assertRefCountEquals(2 + mode, reader1);
473
474       if (mode == 1) {
475         initReader2.close();
476       }
477       
478       modifyIndex(1, dir1);
479       IndexReader reader3 = reader2.reopen();
480       assertRefCountEquals(2 + mode, reader1);
481       assertRefCountEquals(1, reader2);
482       
483       multiReader1.close();
484       assertRefCountEquals(1 + mode, reader1);
485       
486       multiReader1.close();
487       assertRefCountEquals(1 + mode, reader1);
488
489       if (mode == 1) {
490         initReader2.close();
491       }
492       
493       reader1.close();
494       assertRefCountEquals(1, reader1);
495       
496       multiReader2.close();
497       assertRefCountEquals(0, reader1);
498       
499       multiReader2.close();
500       assertRefCountEquals(0, reader1);
501       
502       reader3.close();
503       assertRefCountEquals(0, reader1);
504       assertReaderClosed(reader1, true, false);
505       
506       reader2.close();
507       assertRefCountEquals(0, reader1);
508       assertReaderClosed(reader1, true, false);
509       
510       reader2.close();
511       assertRefCountEquals(0, reader1);
512       
513       reader3.close();
514       assertRefCountEquals(0, reader1);
515       assertReaderClosed(reader1, true, true);
516       dir1.close();
517       dir2.close();
518     }
519
520   }
521
522   public void testReferenceCountingParallelReader() throws IOException {
523     for (int mode = 0; mode <=1; mode++) {
524       Directory dir1 = newDirectory();
525       createIndex(random, dir1, false);
526       Directory dir2 = newDirectory();
527       createIndex(random, dir2, true);
528       
529       IndexReader reader1 = IndexReader.open(dir1, false);
530       assertRefCountEquals(1, reader1);
531       
532       ParallelReader parallelReader1 = new ParallelReader(mode == 0);
533       parallelReader1.add(reader1);
534       IndexReader initReader2 = IndexReader.open(dir2, false);
535       parallelReader1.add(initReader2);
536       modifyIndex(1, dir2);
537       assertRefCountEquals(1 + mode, reader1);
538       
539       IndexReader parallelReader2 = parallelReader1.reopen();
540       // index1 hasn't changed, so parallelReader2 should share reader1 now with multiReader1
541       assertRefCountEquals(2 + mode, reader1);
542       
543       modifyIndex(0, dir1);
544       modifyIndex(0, dir2);
545       IndexReader reader2 = reader1.reopen();
546       assertRefCountEquals(2 + mode, reader1);
547
548       if (mode == 1) {
549         initReader2.close();
550       }
551       
552       modifyIndex(4, dir1);
553       IndexReader reader3 = reader2.reopen();
554       assertRefCountEquals(2 + mode, reader1);
555       assertRefCountEquals(1, reader2);
556       
557       parallelReader1.close();
558       assertRefCountEquals(1 + mode, reader1);
559       
560       parallelReader1.close();
561       assertRefCountEquals(1 + mode, reader1);
562
563       if (mode == 1) {
564         initReader2.close();
565       }
566       
567       reader1.close();
568       assertRefCountEquals(1, reader1);
569       
570       parallelReader2.close();
571       assertRefCountEquals(0, reader1);
572       
573       parallelReader2.close();
574       assertRefCountEquals(0, reader1);
575       
576       reader3.close();
577       assertRefCountEquals(0, reader1);
578       assertReaderClosed(reader1, true, false);
579       
580       reader2.close();
581       assertRefCountEquals(0, reader1);
582       assertReaderClosed(reader1, true, false);
583       
584       reader2.close();
585       assertRefCountEquals(0, reader1);
586       
587       reader3.close();
588       assertRefCountEquals(0, reader1);
589       assertReaderClosed(reader1, true, true);
590
591       dir1.close();
592       dir2.close();
593     }
594
595   }
596   
597   public void testNormsRefCounting() throws IOException {
598     Directory dir1 = newDirectory();
599     createIndex(random, dir1, false);
600     
601     IndexReader reader1 = IndexReader.open(dir1, false);
602     SegmentReader segmentReader1 = SegmentReader.getOnlySegmentReader(reader1);
603     IndexReader modifier = IndexReader.open(dir1, false);
604     modifier.deleteDocument(0);
605     modifier.close();
606     
607     IndexReader reader2 = reader1.reopen();
608     modifier = IndexReader.open(dir1, false);
609     modifier.setNorm(1, "field1", 50);
610     modifier.setNorm(1, "field2", 50);
611     modifier.close();
612     
613     IndexReader reader3 = reader2.reopen();
614     SegmentReader segmentReader3 = SegmentReader.getOnlySegmentReader(reader3);
615     modifier = IndexReader.open(dir1, false);
616     modifier.deleteDocument(2);
617     modifier.close();
618
619     IndexReader reader4 = reader3.reopen();
620     modifier = IndexReader.open(dir1, false);
621     modifier.deleteDocument(3);
622     modifier.close();
623
624     IndexReader reader5 = reader3.reopen();
625     
626     // Now reader2-reader5 references reader1. reader1 and reader2
627     // share the same norms. reader3, reader4, reader5 also share norms.
628     assertRefCountEquals(1, reader1);
629     assertFalse(segmentReader1.normsClosed());
630
631     reader1.close();
632
633     assertRefCountEquals(0, reader1);
634     assertFalse(segmentReader1.normsClosed());
635
636     reader2.close();
637     assertRefCountEquals(0, reader1);
638
639     // now the norms for field1 and field2 should be closed
640     assertTrue(segmentReader1.normsClosed("field1"));
641     assertTrue(segmentReader1.normsClosed("field2"));
642
643     // but the norms for field3 and field4 should still be open
644     assertFalse(segmentReader1.normsClosed("field3"));
645     assertFalse(segmentReader1.normsClosed("field4"));
646     
647     reader3.close();
648     assertRefCountEquals(0, reader1);
649     assertFalse(segmentReader3.normsClosed());
650     reader5.close();
651     assertRefCountEquals(0, reader1);
652     assertFalse(segmentReader3.normsClosed());
653     reader4.close();
654     assertRefCountEquals(0, reader1);
655     
656     // and now all norms that reader1 used should be closed
657     assertTrue(segmentReader1.normsClosed());
658     
659     // now that reader3, reader4 and reader5 are closed,
660     // the norms that those three readers shared should be
661     // closed as well
662     assertTrue(segmentReader3.normsClosed());
663
664     dir1.close();
665   }
666   
667   private void performTestsWithExceptionInReopen(TestReopen test) throws Exception {
668     IndexReader index1 = test.openReader();
669     IndexReader index2 = test.openReader();
670
671     TestIndexReader.assertIndexEquals(index1, index2);
672     
673     try {
674       refreshReader(index1, test, 0, true);
675       fail("Expected exception not thrown.");
676     } catch (Exception e) {
677       // expected exception
678     }
679     
680     // index2 should still be usable and unaffected by the failed reopen() call
681     TestIndexReader.assertIndexEquals(index1, index2);
682
683     index1.close();
684     index2.close();
685   }
686   
687   public void testThreadSafety() throws Exception {
688     final Directory dir = newDirectory();
689     // NOTE: this also controls the number of threads!
690     final int n = _TestUtil.nextInt(random, 20, 40);
691     IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(
692         TEST_VERSION_CURRENT, new MockAnalyzer(random)));
693     for (int i = 0; i < n; i++) {
694       writer.addDocument(createDocument(i, 3));
695     }
696     writer.optimize();
697     writer.close();
698
699     final TestReopen test = new TestReopen() {      
700       @Override
701       protected void modifyIndex(int i) throws IOException {
702         if (i % 3 == 0) {
703           IndexReader modifier = IndexReader.open(dir, false);
704           modifier.setNorm(i, "field1", 50);
705           modifier.close();
706         } else if (i % 3 == 1) {
707           IndexReader modifier = IndexReader.open(dir, false);
708           modifier.deleteDocument(i % modifier.maxDoc());
709           modifier.close();
710         } else {
711           IndexWriter modifier = new IndexWriter(dir, new IndexWriterConfig(
712               TEST_VERSION_CURRENT, new MockAnalyzer(random)));
713           modifier.addDocument(createDocument(n + i, 6));
714           modifier.close();
715         }
716       }
717
718       @Override
719       protected IndexReader openReader() throws IOException {
720         return IndexReader.open(dir, false);
721       }      
722     };
723     
724     final List<ReaderCouple> readers = Collections.synchronizedList(new ArrayList<ReaderCouple>());
725     IndexReader firstReader = IndexReader.open(dir, false);
726     IndexReader reader = firstReader;
727     final Random rnd = random;
728     
729     ReaderThread[] threads = new ReaderThread[n];
730     final Set<IndexReader> readersToClose = Collections.synchronizedSet(new HashSet<IndexReader>());
731     
732     for (int i = 0; i < n; i++) {
733       if (i % 2 == 0) {
734         IndexReader refreshed = reader.reopen();
735         if (refreshed != reader) {
736           readersToClose.add(reader);
737         }
738         reader = refreshed;
739       }
740       final IndexReader r = reader;
741       
742       final int index = i;    
743       
744       ReaderThreadTask task;
745       
746       if (i < 4 || (i >=10 && i < 14) || i > 18) {
747         task = new ReaderThreadTask() {
748           
749           @Override
750           public void run() throws Exception {
751             while (!stopped) {
752               if (index % 2 == 0) {
753                 // refresh reader synchronized
754                 ReaderCouple c = (refreshReader(r, test, index, true));
755                 readersToClose.add(c.newReader);
756                 readersToClose.add(c.refreshedReader);
757                 readers.add(c);
758                 // prevent too many readers
759                 break;
760               } else {
761                 // not synchronized
762                 IndexReader refreshed = r.reopen();
763                 
764                 IndexSearcher searcher = newSearcher(refreshed);
765                 ScoreDoc[] hits = searcher.search(
766                     new TermQuery(new Term("field1", "a" + rnd.nextInt(refreshed.maxDoc()))),
767                     null, 1000).scoreDocs;
768                 if (hits.length > 0) {
769                   searcher.doc(hits[0].doc);
770                 }
771                 searcher.close();
772                 if (refreshed != r) {
773                   refreshed.close();
774                 }
775               }
776               synchronized(this) {
777                 wait(_TestUtil.nextInt(random, 1, 100));
778               }
779             }
780           }
781           
782         };
783       } else {
784         task = new ReaderThreadTask() {
785           @Override
786           public void run() throws Exception {
787             while (!stopped) {
788               int numReaders = readers.size();
789               if (numReaders > 0) {
790                 ReaderCouple c =  readers.get(rnd.nextInt(numReaders));
791                 TestIndexReader.assertIndexEquals(c.newReader, c.refreshedReader);
792               }
793               
794               synchronized(this) {
795                 wait(_TestUtil.nextInt(random, 1, 100));
796               }
797             }
798           }
799         };
800       }
801       
802       threads[i] = new ReaderThread(task);
803       threads[i].start();
804     }
805     
806     synchronized(this) {
807       wait(1000);
808     }
809     
810     for (int i = 0; i < n; i++) {
811       if (threads[i] != null) {
812         threads[i].stopThread();
813       }
814     }
815     
816     for (int i = 0; i < n; i++) {
817       if (threads[i] != null) {
818         threads[i].join();
819         if (threads[i].error != null) {
820           String msg = "Error occurred in thread " + threads[i].getName() + ":\n" + threads[i].error.getMessage();
821           fail(msg);
822         }
823       }
824       
825     }
826     
827     for (final IndexReader readerToClose : readersToClose) {
828       readerToClose.close();
829     }
830     
831     firstReader.close();
832     reader.close();
833     
834     for (final IndexReader readerToClose : readersToClose) {
835       assertReaderClosed(readerToClose, true, true);
836     }
837
838     assertReaderClosed(reader, true, true);
839     assertReaderClosed(firstReader, true, true);
840
841     dir.close();
842   }
843   
844   private static class ReaderCouple {
845     ReaderCouple(IndexReader r1, IndexReader r2) {
846       newReader = r1;
847       refreshedReader = r2;
848     }
849     
850     IndexReader newReader;
851     IndexReader refreshedReader;
852   }
853   
854   private abstract static class ReaderThreadTask {
855     protected volatile boolean stopped;
856     public void stop() {
857       this.stopped = true;
858     }
859     
860     public abstract void run() throws Exception;
861   }
862   
863   private static class ReaderThread extends Thread {
864     private ReaderThreadTask task;
865     private Throwable error;
866     
867     
868     ReaderThread(ReaderThreadTask task) {
869       this.task = task;
870     }
871     
872     public void stopThread() {
873       this.task.stop();
874     }
875     
876     @Override
877     public void run() {
878       try {
879         this.task.run();
880       } catch (Throwable r) {
881         r.printStackTrace(System.out);
882         this.error = r;
883       }
884     }
885   }
886   
887   private Object createReaderMutex = new Object();
888   
889   private ReaderCouple refreshReader(IndexReader reader, boolean hasChanges) throws IOException {
890     return refreshReader(reader, null, -1, hasChanges);
891   }
892   
893   ReaderCouple refreshReader(IndexReader reader, TestReopen test, int modify, boolean hasChanges) throws IOException {
894     synchronized (createReaderMutex) {
895       IndexReader r = null;
896       if (test != null) {
897         test.modifyIndex(modify);
898         r = test.openReader();
899       }
900       
901       IndexReader refreshed = null;
902       try {
903         refreshed = reader.reopen();
904       } finally {
905         if (refreshed == null && r != null) {
906           // Hit exception -- close opened reader
907           r.close();
908         }
909       }
910       
911       if (hasChanges) {
912         if (refreshed == reader) {
913           fail("No new IndexReader instance created during refresh.");
914         }
915       } else {
916         if (refreshed != reader) {
917           fail("New IndexReader instance created during refresh even though index had no changes.");
918         }
919       }
920       
921       return new ReaderCouple(r, refreshed);
922     }
923   }
924   
925   public static void createIndex(Random random, Directory dir, boolean multiSegment) throws IOException {
926     IndexWriter.unlock(dir);
927     IndexWriter w = new IndexWriter(dir, LuceneTestCase.newIndexWriterConfig(random,
928         TEST_VERSION_CURRENT, new MockAnalyzer(random))
929         .setMergePolicy(new LogDocMergePolicy()));
930     
931     for (int i = 0; i < 100; i++) {
932       w.addDocument(createDocument(i, 4));
933       if (multiSegment && (i % 10) == 0) {
934         w.commit();
935       }
936     }
937     
938     if (!multiSegment) {
939       w.optimize();
940     }
941     
942     w.close();
943
944     IndexReader r = IndexReader.open(dir, false);
945     if (multiSegment) {
946       assertTrue(r.getSequentialSubReaders().length > 1);
947     } else {
948       assertTrue(r.getSequentialSubReaders().length == 1);
949     }
950     r.close();
951   }
952
953   public static Document createDocument(int n, int numFields) {
954     StringBuilder sb = new StringBuilder();
955     Document doc = new Document();
956     sb.append("a");
957     sb.append(n);
958     doc.add(new Field("field1", sb.toString(), Store.YES, Index.ANALYZED));
959     doc.add(new Field("fielda", sb.toString(), Store.YES, Index.NOT_ANALYZED_NO_NORMS));
960     doc.add(new Field("fieldb", sb.toString(), Store.YES, Index.NO));
961     sb.append(" b");
962     sb.append(n);
963     for (int i = 1; i < numFields; i++) {
964       doc.add(new Field("field" + (i+1), sb.toString(), Store.YES, Index.ANALYZED));
965     }
966     return doc;
967   }
968
969   static void modifyIndex(int i, Directory dir) throws IOException {
970     switch (i) {
971       case 0: {
972         if (VERBOSE) {
973           System.out.println("TEST: modify index");
974         }
975         IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
976         w.setInfoStream(VERBOSE ? System.out : null);
977         w.deleteDocuments(new Term("field2", "a11"));
978         w.deleteDocuments(new Term("field2", "b30"));
979         w.close();
980         break;
981       }
982       case 1: {
983         IndexReader reader = IndexReader.open(dir, false);
984         reader.setNorm(4, "field1", 123);
985         reader.setNorm(44, "field2", 222);
986         reader.setNorm(44, "field4", 22);
987         reader.close();
988         break;
989       }
990       case 2: {
991         IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
992         w.optimize();
993         w.close();
994         break;
995       }
996       case 3: {
997         IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
998         w.addDocument(createDocument(101, 4));
999         w.optimize();
1000         w.addDocument(createDocument(102, 4));
1001         w.addDocument(createDocument(103, 4));
1002         w.close();
1003         break;
1004       }
1005       case 4: {
1006         IndexReader reader = IndexReader.open(dir, false);
1007         reader.setNorm(5, "field1", 123);
1008         reader.setNorm(55, "field2", 222);
1009         reader.close();
1010         break;
1011       }
1012       case 5: {
1013         IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
1014         w.addDocument(createDocument(101, 4));
1015         w.close();
1016         break;
1017       }
1018     }
1019   }  
1020   
1021   private void assertReaderClosed(IndexReader reader, boolean checkSubReaders, boolean checkNormsClosed) {
1022     assertEquals(0, reader.getRefCount());
1023     
1024     if (checkNormsClosed && reader instanceof SegmentReader) {
1025       assertTrue(((SegmentReader) reader).normsClosed());
1026     }
1027     
1028     if (checkSubReaders) {
1029       if (reader instanceof DirectoryReader) {
1030         IndexReader[] subReaders = reader.getSequentialSubReaders();
1031         for (int i = 0; i < subReaders.length; i++) {
1032           assertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed);
1033         }
1034       }
1035       
1036       if (reader instanceof MultiReader) {
1037         IndexReader[] subReaders = reader.getSequentialSubReaders();
1038         for (int i = 0; i < subReaders.length; i++) {
1039           assertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed);
1040         }
1041       }
1042       
1043       if (reader instanceof ParallelReader) {
1044         IndexReader[] subReaders = ((ParallelReader) reader).getSubReaders();
1045         for (int i = 0; i < subReaders.length; i++) {
1046           assertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed);
1047         }
1048       }
1049     }
1050   }
1051
1052   /*
1053   private void assertReaderOpen(IndexReader reader) {
1054     reader.ensureOpen();
1055     
1056     if (reader instanceof DirectoryReader) {
1057       IndexReader[] subReaders = reader.getSequentialSubReaders();
1058       for (int i = 0; i < subReaders.length; i++) {
1059         assertReaderOpen(subReaders[i]);
1060       }
1061     }
1062   }
1063   */
1064
1065   private void assertRefCountEquals(int refCount, IndexReader reader) {
1066     assertEquals("Reader has wrong refCount value.", refCount, reader.getRefCount());
1067   }
1068
1069
1070   private abstract static class TestReopen {
1071     protected abstract IndexReader openReader() throws IOException;
1072     protected abstract void modifyIndex(int i) throws IOException;
1073   }
1074   
1075   public void testCloseOrig() throws Throwable {
1076     Directory dir = newDirectory();
1077     createIndex(random, dir, false);
1078     IndexReader r1 = IndexReader.open(dir, false);
1079     IndexReader r2 = IndexReader.open(dir, false);
1080     r2.deleteDocument(0);
1081     r2.close();
1082
1083     IndexReader r3 = r1.reopen();
1084     assertTrue(r1 != r3);
1085     r1.close();
1086     try {
1087       r1.document(2);
1088       fail("did not hit exception");
1089     } catch (AlreadyClosedException ace) {
1090       // expected
1091     }
1092     r3.close();
1093     dir.close();
1094   }
1095
1096   public void testDeletes() throws Throwable {
1097     Directory dir = newDirectory();
1098     createIndex(random, dir, false); // Create an index with a bunch of docs (1 segment)
1099
1100     modifyIndex(0, dir); // Get delete bitVector on 1st segment
1101     modifyIndex(5, dir); // Add a doc (2 segments)
1102
1103     IndexReader r1 = IndexReader.open(dir, false); // MSR
1104
1105     modifyIndex(5, dir); // Add another doc (3 segments)
1106
1107     IndexReader r2 = r1.reopen(); // MSR
1108     assertTrue(r1 != r2);
1109
1110     SegmentReader sr1 = (SegmentReader) r1.getSequentialSubReaders()[0]; // Get SRs for the first segment from original
1111     SegmentReader sr2 = (SegmentReader) r2.getSequentialSubReaders()[0]; // and reopened IRs
1112
1113     // At this point they share the same BitVector
1114     assertTrue(sr1.deletedDocs==sr2.deletedDocs);
1115
1116     r2.deleteDocument(0);
1117
1118     // r1 should not see the delete
1119     assertFalse(r1.isDeleted(0));
1120
1121     // Now r2 should have made a private copy of deleted docs:
1122     assertTrue(sr1.deletedDocs!=sr2.deletedDocs);
1123
1124     r1.close();
1125     r2.close();
1126     dir.close();
1127   }
1128
1129   public void testDeletes2() throws Throwable {
1130     Directory dir = newDirectory();
1131     createIndex(random, dir, false);
1132     // Get delete bitVector
1133     modifyIndex(0, dir);
1134     IndexReader r1 = IndexReader.open(dir, false);
1135
1136     // Add doc:
1137     modifyIndex(5, dir);
1138
1139     IndexReader r2 = r1.reopen();
1140     assertTrue(r1 != r2);
1141
1142     IndexReader[] rs2 = r2.getSequentialSubReaders();
1143
1144     SegmentReader sr1 = SegmentReader.getOnlySegmentReader(r1);
1145     SegmentReader sr2 = (SegmentReader) rs2[0];
1146
1147     // At this point they share the same BitVector
1148     assertTrue(sr1.deletedDocs==sr2.deletedDocs);
1149     final BitVector delDocs = sr1.deletedDocs;
1150     r1.close();
1151
1152     r2.deleteDocument(0);
1153     assertTrue(delDocs==sr2.deletedDocs);
1154     r2.close();
1155     dir.close();
1156   }
1157
1158   private static class KeepAllCommits implements IndexDeletionPolicy {
1159     public void onInit(List<? extends IndexCommit> commits) {
1160     }
1161     public void onCommit(List<? extends IndexCommit> commits) {
1162     }
1163   }
1164
1165   public void testReopenOnCommit() throws Throwable {
1166     Directory dir = newDirectory();
1167     IndexWriter writer = new IndexWriter(
1168         dir,
1169         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
1170             setIndexDeletionPolicy(new KeepAllCommits()).
1171             setMaxBufferedDocs(-1).
1172             setMergePolicy(newLogMergePolicy(10))
1173     );
1174     for(int i=0;i<4;i++) {
1175       Document doc = new Document();
1176       doc.add(newField("id", ""+i, Field.Store.NO, Field.Index.NOT_ANALYZED));
1177       writer.addDocument(doc);
1178       Map<String,String> data = new HashMap<String,String>();
1179       data.put("index", i+"");
1180       writer.commit(data);
1181     }
1182     for(int i=0;i<4;i++) {
1183       writer.deleteDocuments(new Term("id", ""+i));
1184       Map<String,String> data = new HashMap<String,String>();
1185       data.put("index", (4+i)+"");
1186       writer.commit(data);
1187     }
1188     writer.close();
1189
1190     IndexReader r = IndexReader.open(dir, false);
1191     assertEquals(0, r.numDocs());
1192
1193     Collection<IndexCommit> commits = IndexReader.listCommits(dir);
1194     for (final IndexCommit commit : commits) {
1195       IndexReader r2 = r.reopen(commit);
1196       assertTrue(r2 != r);
1197
1198       // Reader should be readOnly
1199       try {
1200         r2.deleteDocument(0);
1201         fail("no exception hit");
1202       } catch (UnsupportedOperationException uoe) {
1203         // expected
1204       }
1205
1206       final Map<String,String> s = commit.getUserData();
1207       final int v;
1208       if (s.size() == 0) {
1209         // First commit created by IW
1210         v = -1;
1211       } else {
1212         v = Integer.parseInt(s.get("index"));
1213       }
1214       if (v < 4) {
1215         assertEquals(1+v, r2.numDocs());
1216       } else {
1217         assertEquals(7-v, r2.numDocs());
1218       }
1219       r.close();
1220       r = r2;
1221     }
1222     r.close();
1223     dir.close();
1224   }
1225   
1226   // LUCENE-1579: Make sure all SegmentReaders are new when
1227   // reopen switches readOnly
1228   public void testReopenChangeReadonly() throws Exception {
1229     Directory dir = newDirectory();
1230     IndexWriter writer = new IndexWriter(
1231         dir,
1232         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
1233             setMaxBufferedDocs(-1).
1234             setMergePolicy(newLogMergePolicy(10))
1235     );
1236     Document doc = new Document();
1237     doc.add(newField("number", "17", Field.Store.NO, Field.Index.NOT_ANALYZED));
1238     writer.addDocument(doc);
1239     writer.commit();
1240
1241     // Open reader1
1242     IndexReader r = IndexReader.open(dir, false);
1243     assertTrue(r instanceof DirectoryReader);
1244     IndexReader r1 = SegmentReader.getOnlySegmentReader(r);
1245     final int[] ints = FieldCache.DEFAULT.getInts(r1, "number");
1246     assertEquals(1, ints.length);
1247     assertEquals(17, ints[0]);
1248
1249     // Reopen to readonly w/ no chnages
1250     IndexReader r3 = r.reopen(true);
1251     assertTrue(r3 instanceof ReadOnlyDirectoryReader);
1252     r3.close();
1253
1254     // Add new segment
1255     writer.addDocument(doc);
1256     writer.commit();
1257
1258     // Reopen reader1 --> reader2
1259     IndexReader r2 = r.reopen(true);
1260     r.close();
1261     assertTrue(r2 instanceof ReadOnlyDirectoryReader);
1262     IndexReader[] subs = r2.getSequentialSubReaders();
1263     final int[] ints2 = FieldCache.DEFAULT.getInts(subs[0], "number");
1264     r2.close();
1265
1266     assertTrue(subs[0] instanceof ReadOnlySegmentReader);
1267     assertTrue(subs[1] instanceof ReadOnlySegmentReader);
1268     assertTrue(ints == ints2);
1269
1270     writer.close();
1271     dir.close();
1272   }
1273 }